# variable_data_row.py
#
# Copyright 2025 Pavel Baksy
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later

from gi.repository import Gtk, GObject, GLib
from datetime import datetime, timedelta


@Gtk.Template(resource_path='/cz/bugsy/roster/widgets/variable-data-row.ui')
class VariableDataRow(Gtk.Box):
    """Widget for a data row with variable name and values for each environment."""

    __gtype_name__ = 'VariableDataRow'

    variable_cell = Gtk.Template.Child()
    name_entry = Gtk.Template.Child()
    edit_buttons_revealer = Gtk.Template.Child()
    cancel_button = Gtk.Template.Child()
    save_button = Gtk.Template.Child()
    sensitive_toggle = Gtk.Template.Child()
    delete_button = Gtk.Template.Child()
    values_box = Gtk.Template.Child()

    __gsignals__ = {
        'variable-changed': (GObject.SIGNAL_RUN_FIRST, None, ()),
        'variable-delete-requested': (GObject.SIGNAL_RUN_FIRST, None, ()),
        'value-changed': (GObject.SIGNAL_RUN_FIRST, None, (str, str)),  # env_id, value
        'sensitivity-changed': (GObject.SIGNAL_RUN_FIRST, None, (bool,)),  # is_sensitive
    }

    def __init__(self, variable_name, environments, size_group, project, project_manager, update_timestamp=None):
        super().__init__()
        self.variable_name = variable_name
        self.original_name = variable_name  # Store original name for cancel
        self.environments = environments
        self.size_group = size_group
        self.project = project
        self.project_manager = project_manager
        self.value_entries = {}
        self.update_timeout_id = None
        self._updating_toggle = False  # Flag to prevent recursion
        self._is_editing = False  # Track editing state

        # Set variable name
        self.name_entry.set_text(variable_name)

        # Connect focus events for inline editing
        focus_controller = Gtk.EventControllerFocus()
        focus_controller.connect('enter', self._on_name_entry_focus_in)
        focus_controller.connect('leave', self._on_name_entry_focus_out)
        self.name_entry.add_controller(focus_controller)

        # Connect activate signal (Enter key)
        self.name_entry.connect('activate', self._on_name_entry_activate)

        # Set initial sensitivity state
        is_sensitive = variable_name in self.project.sensitive_variables
        self._updating_toggle = True
        self.sensitive_toggle.set_active(is_sensitive)
        self._update_sensitive_icon(is_sensitive)
        self._updating_toggle = False

        # Add variable cell to size group for alignment
        if size_group:
            size_group.add_widget(self.variable_cell)

        self._populate_values()

        # Apply visual marking if recently updated
        if update_timestamp:
            self._apply_update_marking(update_timestamp)

    def _populate_values(self):
        """Populate value entries for each environment."""
        # Clear existing
        while child := self.values_box.get_first_child():
            self.values_box.remove(child)

        self.value_entries = {}

        # Check if this variable is sensitive
        is_sensitive = self.variable_name in self.project.sensitive_variables

        # Create entry for each environment
        for env in self.environments:
            # Create a box to hold the entry (for size group alignment)
            entry_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
            entry_box.set_size_request(200, -1)

            entry = Gtk.Entry()
            entry.set_placeholder_text(env.name)
            entry.set_hexpand(True)

            # Configure entry for sensitive variables
            if is_sensitive:
                entry.set_visibility(False)  # Show bullets instead of text
                entry.set_input_purpose(Gtk.InputPurpose.PASSWORD)
                entry.add_css_class("sensitive-variable")

            entry_box.append(entry)

            # Add entry box to size group for alignment
            if self.size_group:
                self.size_group.add_widget(entry_box)

            self.values_box.append(entry_box)
            self.value_entries[env.id] = entry

            # Get value from appropriate storage (keyring or JSON) asynchronously
            def on_value_retrieved(value, entry=entry, env_id=env.id):
                entry.set_text(value)
                # Connect changed handler after setting initial value to avoid spurious signals
                entry.connect('changed', self._on_value_changed, env_id)

            self.project_manager.get_variable_value(
                self.project.id,
                env.id,
                self.variable_name,
                on_value_retrieved
            )

    def _on_value_changed(self, entry, env_id):
        """Handle value entry changes."""
        value = entry.get_text()

        # Emit signal first (value is being changed)
        self.emit('value-changed', env_id, value)

        # Use project_manager to store value in appropriate location (async)
        self.project_manager.set_variable_value(
            self.project.id,
            env_id,
            self.variable_name,
            value,
            callback=None  # Fire and forget - errors are logged
        )

    def _on_name_entry_focus_in(self, controller):
        """Show edit buttons when entry gains focus."""
        self._is_editing = True
        self.original_name = self.name_entry.get_text().strip()
        self.edit_buttons_revealer.set_reveal_child(True)

    def _on_name_entry_focus_out(self, controller):
        """Handle focus leaving the entry without explicit save/cancel."""
        # Only auto-hide if user didn't click cancel/save
        # The buttons will handle hiding themselves
        pass

    def _on_name_entry_activate(self, entry):
        """Handle Enter key press - same as clicking Save."""
        self._save_changes()

    @Gtk.Template.Callback()
    def on_cancel_clicked(self, button):
        """Cancel editing and restore original name."""
        self.name_entry.set_text(self.original_name)
        self._is_editing = False
        self.edit_buttons_revealer.set_reveal_child(False)

    @Gtk.Template.Callback()
    def on_save_clicked(self, button):
        """Save variable name changes."""
        self._save_changes()

    def _save_changes(self):
        """Save the variable name change."""
        new_name = self.name_entry.get_text().strip()

        # Only emit if name actually changed
        if new_name != self.original_name:
            self.emit('variable-changed')

        self._is_editing = False
        self.edit_buttons_revealer.set_reveal_child(False)
        self.original_name = new_name

    @Gtk.Template.Callback()
    def on_delete_clicked(self, button):
        """Handle delete button click."""
        self.emit('variable-delete-requested')

    @Gtk.Template.Callback()
    def on_sensitive_toggled(self, toggle_button):
        """Handle sensitivity toggle."""
        if self._updating_toggle:
            return

        is_sensitive = toggle_button.get_active()
        self._update_sensitive_icon(is_sensitive)

        # Emit signal so parent can update the project
        self.emit('sensitivity-changed', is_sensitive)

    def _update_sensitive_icon(self, is_sensitive):
        """Update the lock icon based on sensitivity state."""
        if is_sensitive:
            self.sensitive_toggle.set_icon_name("channel-secure-symbolic")
            self.sensitive_toggle.set_tooltip_text("Sensitive (stored in keyring)\nClick to make non-sensitive")
            self.name_entry.add_css_class("sensitive-variable-name")
        else:
            self.sensitive_toggle.set_icon_name("changes-allow-symbolic")
            self.sensitive_toggle.set_tooltip_text("Not sensitive (stored in JSON)\nClick to mark as sensitive")
            self.name_entry.remove_css_class("sensitive-variable-name")

    def get_variable_name(self):
        """Return current variable name."""
        return self.name_entry.get_text().strip()

    def refresh(self, environments, project):
        """Refresh with updated environments and project."""
        self.environments = environments
        self.project = project

        # Update original_name in case variable was renamed
        self.original_name = self.variable_name

        # Update sensitivity toggle state
        is_sensitive = self.variable_name in self.project.sensitive_variables
        self._updating_toggle = True
        self.sensitive_toggle.set_active(is_sensitive)
        self._update_sensitive_icon(is_sensitive)
        self._updating_toggle = False

        self._populate_values()

    def _apply_update_marking(self, timestamp_str):
        """
        Apply visual marking to show variable was recently updated.

        Args:
            timestamp_str: ISO format timestamp of when variable was updated
        """
        try:
            # Parse timestamp
            update_time = datetime.fromisoformat(timestamp_str)
            time_diff = datetime.now() - update_time

            # Only apply marking if updated within last 3 minutes
            if time_diff < timedelta(minutes=3):
                # Add CSS class for visual styling
                self.add_css_class('recently-updated')

                # Set tooltip with update time
                seconds_ago = int(time_diff.total_seconds())
                if seconds_ago < 10:
                    tooltip_text = "✨ Updated just now by script"
                elif seconds_ago < 60:
                    tooltip_text = f"✨ Updated {seconds_ago} seconds ago by script"
                else:
                    minutes_ago = seconds_ago // 60
                    if minutes_ago == 1:
                        tooltip_text = "✨ Updated 1 minute ago by script"
                    else:
                        tooltip_text = f"✨ Updated {minutes_ago} minutes ago by script"

                self.set_tooltip_text(tooltip_text)

                # Keep highlighting visible for 60 seconds (plenty of time to see it)
                if self.update_timeout_id:
                    GLib.source_remove(self.update_timeout_id)

                self.update_timeout_id = GLib.timeout_add_seconds(60, self._remove_update_marking)

        except (ValueError, AttributeError):
            # Invalid timestamp format, skip marking
            pass

    def _remove_update_marking(self):
        """Remove visual marking after timeout."""
        self.remove_css_class('recently-updated')
        self.set_tooltip_text("")
        self.update_timeout_id = None
        return False  # Don't repeat timeout
