"""
Edit Popup Module
Handles image editing/annotation functionality for the signage player
"""

import os
import threading
from datetime import datetime
import json
import re
import shutil
import time

from kivy.uix.widget import Widget
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.graphics import Color, Line, RoundedRectangle
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.logger import Logger
from kivy.uix.video import Video


class DrawingLayer(Widget):
    """Layer for drawing on top of images"""
    def __init__(self, reset_callback=None, **kwargs):
        super(DrawingLayer, self).__init__(**kwargs)
        self.strokes = []  # Store all drawn lines
        self.current_color = (1, 0, 0, 1)  # Default red
        self.current_width = 3  # Default thickness
        self.drawing_enabled = True  # Drawing always enabled in edit mode
        self.reset_callback = reset_callback  # Callback to reset countdown timer
        self._last_draw_time = 0  # For throttling touch updates
        self._draw_throttle_interval = 0.016  # ~60fps (16ms between updates)
    
    def on_touch_down(self, touch):
        if not self.drawing_enabled or not self.collide_point(*touch.pos):
            return False
        
        # Reset countdown on user interaction
        if self.reset_callback:
            self.reset_callback()
        
        with self.canvas:
            Color(*self.current_color)
            new_line = Line(points=[touch.x, touch.y], width=self.current_width)
            self.strokes.append({'line': new_line, 'color': self.current_color, 'width': self.current_width})
        touch.ud['line'] = new_line
        return True
    
    def on_touch_move(self, touch):
        if 'line' in touch.ud and self.drawing_enabled:
            # Throttle updates to ~60fps for better performance
            current_time = time.time()
            if current_time - self._last_draw_time >= self._draw_throttle_interval:
                touch.ud['line'].points += [touch.x, touch.y]
                self._last_draw_time = current_time
        return True
    
    def undo(self):
        """Remove the last stroke"""
        if self.strokes:
            last_stroke = self.strokes.pop()
            self.canvas.remove(last_stroke['line'])
            Logger.info("DrawingLayer: Undid last stroke")
    
    def clear_all(self):
        """Clear all strokes"""
        for stroke in self.strokes:
            self.canvas.remove(stroke['line'])
        self.strokes = []
        Logger.info("DrawingLayer: Cleared all strokes")
    
    def set_color(self, color_tuple):
        """Set drawing color (RGBA)"""
        self.current_color = color_tuple
    
    def set_thickness(self, value):
        """Set line thickness"""
        self.current_width = value


class EditPopup(Popup):
    """Popup for editing/annotating images"""
    def __init__(self, player_instance, image_path, user_card_data=None, **kwargs):
        super(EditPopup, self).__init__(**kwargs)
        self.player = player_instance
        self.image_path = image_path
        self.user_card_data = user_card_data  # Store card data to send to server on save
        
        # Auto-close timer (5 minutes)
        self.auto_close_timeout = 300  # 5 minutes in seconds
        self.remaining_time = self.auto_close_timeout
        self.countdown_event = None
        self.auto_close_event = None
        
        # Pause playback (without auto-resume timer)
        self.was_paused = self.player.is_paused
        if not self.was_paused:
            self.player.is_paused = True
            Clock.unschedule(self.player.next_media)
            
            # Cancel auto-resume timer if one exists (don't want auto-resume during editing)
            if self.player.auto_resume_event:
                Clock.unschedule(self.player.auto_resume_event)
                self.player.auto_resume_event = None
                Logger.info("EditPopup: Cancelled auto-resume timer")
            
            # Update button icon to play (to show it's paused)
            self.player.ids.play_pause_btn.background_normal = self.player.resources_path + '/play.png'
            self.player.ids.play_pause_btn.background_down = self.player.resources_path + '/play.png'
            
            if self.player.current_widget and isinstance(self.player.current_widget, Video):
                self.player.current_widget.state = 'pause'
            
            Logger.info("EditPopup: ⏸ Paused playback (no auto-resume) for editing")
        
        # Show cursor
        try:
            Window.show_cursor = True
        except:
            pass
        
        # Note: UI is now defined in KV file, but we need to customize after creation
        # Set image source after KV loads
        Clock.schedule_once(lambda dt: self._setup_after_kv(), 0)
    
    def _setup_after_kv(self):
        """Setup widgets after KV file has loaded them"""
        # Set the image source
        self.ids.image_widget.source = self.image_path
        
        # Create and insert drawing layer (custom class, must be added programmatically)
        self.drawing_layer = DrawingLayer(
            reset_callback=self.reset_countdown,
            size_hint=(1, 1),
            pos_hint={'x': 0, 'y': 0}
        )
        # Replace placeholder with actual drawing layer
        content = self.content
        placeholder_index = content.children.index(self.ids.drawing_layer_placeholder)
        content.remove_widget(self.ids.drawing_layer_placeholder)
        content.add_widget(self.drawing_layer, index=placeholder_index)
        
        # Set icon sources
        pen_icon_path = os.path.join(self.player.resources_path, 'edit-pen.png')
        self.ids.color_icon.source = pen_icon_path
        self.ids.thickness_icon.source = pen_icon_path
        
        # Bind button callbacks
        self.ids.undo_btn.bind(on_press=lambda x: (self.reset_countdown(), self.drawing_layer.undo()))
        self.ids.clear_btn.bind(on_press=lambda x: (self.reset_countdown(), self.drawing_layer.clear_all()))
        self.ids.save_btn.bind(on_press=self.save_image)
        self.ids.cancel_btn.bind(on_press=self.close_without_saving)
        
        # Bind color buttons
        self.ids.red_btn.bind(on_press=lambda x: self.drawing_layer.set_color((1, 0, 0, 1)))
        self.ids.blue_btn.bind(on_press=lambda x: self.drawing_layer.set_color((0, 0, 1, 1)))
        self.ids.green_btn.bind(on_press=lambda x: self.drawing_layer.set_color((0, 1, 0, 1)))
        self.ids.black_btn.bind(on_press=lambda x: self.drawing_layer.set_color((0, 0, 0, 1)))
        self.ids.white_btn.bind(on_press=lambda x: self.drawing_layer.set_color((1, 1, 1, 1)))
        
        # Bind thickness buttons
        self.ids.small_btn.bind(on_press=lambda x: self.drawing_layer.set_thickness(2))
        self.ids.medium_btn.bind(on_press=lambda x: self.drawing_layer.set_thickness(5))
        self.ids.large_btn.bind(on_press=lambda x: self.drawing_layer.set_thickness(10))
        
        # Add rounded corners to buttons
        for btn_id in ['undo_btn', 'clear_btn', 'save_btn', 'cancel_btn']:
            btn = self.ids[btn_id]
            btn.bind(pos=self._make_rounded_btn, size=self._make_rounded_btn)
        
        # Add circular corners to color/thickness buttons
        for btn_id in ['red_btn', 'blue_btn', 'green_btn', 'black_btn', 'white_btn',
                       'small_btn', 'medium_btn', 'large_btn']:
            btn = self.ids[btn_id]
            btn.bind(pos=self._make_round, size=self._make_round)
        
        # Reference to countdown label
        self.countdown_label = self.ids.countdown_label
        
        # Bind to dismiss
        self.bind(on_dismiss=self.on_popup_dismiss)
        
        # Start countdown timer (updates every second)
        self.countdown_event = Clock.schedule_interval(self.update_countdown, 1)
        
        # Start auto-close timer (closes after 5 minutes)
        self.auto_close_event = Clock.schedule_once(self.auto_close, self.auto_close_timeout)
        
        Logger.info(f"EditPopup: Opened for image {os.path.basename(self.image_path)} (auto-close in 5 minutes)")
    
    def update_countdown(self, dt):
        """Update countdown display"""
        self.remaining_time -= 1
        
        # Format time as MM:SS
        minutes = self.remaining_time // 60
        seconds = self.remaining_time % 60
        self.countdown_label.text = f"{minutes}:{seconds:02d}"
        
        # Change color as time runs out
        if self.remaining_time <= 60:  # Last minute - red
            self.countdown_label.color = (1, 0.2, 0.2, 1)
        elif self.remaining_time <= 120:  # Last 2 minutes - yellow
            self.countdown_label.color = (1, 1, 0, 1)
        else:
            self.countdown_label.color = (1, 1, 1, 1)  # White
        
        if self.remaining_time <= 0:
            Clock.unschedule(self.countdown_event)
    
    def reset_countdown(self):
        """Reset countdown timer on user interaction"""
        self.remaining_time = self.auto_close_timeout
        
        # Cancel existing timers
        if self.countdown_event:
            Clock.unschedule(self.countdown_event)
        if self.auto_close_event:
            Clock.unschedule(self.auto_close_event)
        
        # Restart timers
        self.countdown_event = Clock.schedule_interval(self.update_countdown, 1)
        self.auto_close_event = Clock.schedule_once(self.auto_close, self.auto_close_timeout)
        
        # Reset color to white
        self.countdown_label.color = (1, 1, 1, 1)
        
        Logger.info("EditPopup: Countdown reset to 5:00")
    
    def auto_close(self, dt):
        """Auto-close the edit popup after timeout"""
        Logger.info("EditPopup: Auto-closing after 5 minutes of inactivity")
        self.close_without_saving(None)
    
    def _make_rounded_btn(self, instance, value):
        """Make toolbar button with slightly rounded corners"""
        instance.canvas.before.clear()
        with instance.canvas.before:
            Color(*instance.background_color)
            instance.round_rect = RoundedRectangle(
                pos=instance.pos,
                size=instance.size,
                radius=[10]
            )
    
    def _make_round(self, instance, value):
        """Make sidebar button fully circular"""
        instance.canvas.before.clear()
        with instance.canvas.before:
            Color(*instance.background_color)
            instance.round_rect = RoundedRectangle(
                pos=instance.pos,
                size=instance.size,
                radius=[instance.height / 2]
            )
    
    def save_image(self, instance):
        """Save the edited image"""
        try:
            # Create edited_media directory if it doesn't exist
            edited_dir = os.path.join(self.player.base_dir, 'media', 'edited_media')
            os.makedirs(edited_dir, exist_ok=True)
            
            # Get original filename
            base_name = os.path.basename(self.image_path)
            name, ext = os.path.splitext(base_name)
            
            # Determine version number
            version_match = re.search(r'_e_v(\d+)$', name)
            if version_match:
                # Increment existing version
                current_version = int(version_match.group(1))
                new_version = current_version + 1
                # Remove old version suffix
                original_name = re.sub(r'_e_v\d+$', '', name)
                new_name = f"{original_name}_e_v{new_version}"
            else:
                # First edit version
                original_name = name
                new_name = f"{name}_e_v1"
            
            # Generate output path
            output_filename = f"{new_name}.jpg"
            output_path = os.path.join(edited_dir, output_filename)
            
            # Temporarily hide toolbars
            self.ids.top_toolbar.opacity = 0
            self.ids.right_sidebar.opacity = 0
            
            # Force canvas update
            self.content.canvas.ask_update()
            
            # Small delay to ensure rendering is complete
            def do_export(dt):
                try:
                    # Export only the visible content (image + drawings, no toolbars)
                    self.content.export_to_png(output_path)
                    
                    Logger.info(f"EditPopup: Saved edited image to {output_path}")
                    
                    # ALSO overwrite the original image with edited content
                    Logger.info(f"EditPopup: Overwriting original image at {self.image_path}")
                    
                    # Get original file info before overwrite
                    orig_size = os.path.getsize(self.image_path)
                    orig_mtime = os.path.getmtime(self.image_path)
                    
                    # Overwrite the file
                    shutil.copy2(output_path, self.image_path)
                    
                    # Force file system sync to ensure data is written to disk
                    os.sync()
                    
                    # Verify the overwrite
                    new_size = os.path.getsize(self.image_path)
                    new_mtime = os.path.getmtime(self.image_path)
                    
                    Logger.info(f"EditPopup: ✓ File overwritten:")
                    Logger.info(f"  - Size: {orig_size} -> {new_size} bytes (changed: {new_size != orig_size})")
                    Logger.info(f"  - Modified time: {orig_mtime} -> {new_mtime} (changed: {new_mtime > orig_mtime})")
                    Logger.info(f"EditPopup: ✓ File synced to disk")
                    
                    # Restore toolbars
                    self.ids.top_toolbar.opacity = 1
                    self.ids.right_sidebar.opacity = 1
                    
                    # Create and save metadata
                    json_filename = self._save_metadata(edited_dir, new_name, base_name, 
                                       new_version if version_match else 1, output_filename)
                    
                    # Upload to server in background (continues after popup closes)
                    upload_thread = threading.Thread(
                        target=self._upload_to_server,
                        args=(output_path, json_filename),
                        daemon=True
                    )
                    upload_thread.start()  # Re-enabled
                    
                    # NOW show saving popup AFTER everything is done
                    def show_saving_and_dismiss(dt):
                        # Create label with background showing detailed save status
                        save_msg = (
                            'Saved locally!\n'
                            'Uploading to server...'
                        )
                        save_label = Label(
                            text=save_msg,
                            font_size='24sp',
                            color=(1, 1, 1, 1),
                            bold=True
                        )
                        
                        saving_popup = Popup(
                            title='',
                            content=save_label,
                            size_hint=(0.85, 0.4),
                            auto_dismiss=False,
                            separator_height=0,
                            background_color=(0.2, 0.7, 0.2, 0.95)  # Green background
                        )
                        saving_popup.open()
                        Logger.info("EditPopup: Saving confirmation popup opened")
                        
                        # Update message after 3 seconds to show upload is happening
                        def update_message(dt):
                            if saving_popup:
                                save_label.text = (
                                    '✓ Saved to device\n'
                                    'Upload in progress...'
                                )
                        
                        Clock.schedule_once(update_message, 3.0)
                        
                        # Dismiss both popups after 4 seconds
                        def dismiss_all(dt):
                            saving_popup.dismiss()
                            Logger.info(f"EditPopup: Dismissing to resume playback...")
                            self.dismiss()
                        
                        Clock.schedule_once(dismiss_all, 4.0)
                    
                    # Small delay to ensure UI is ready, then show popup
                    Clock.schedule_once(show_saving_and_dismiss, 0.1)
                    
                except Exception as e:
                    Logger.error(f"EditPopup: Error in export: {e}")
                    import traceback
                    Logger.error(f"EditPopup: Traceback: {traceback.format_exc()}")
                    self.title = f'Error saving: {e}'
                    # Restore toolbars
                    self.ids.top_toolbar.opacity = 1
                    self.ids.right_sidebar.opacity = 1
                    # Still dismiss on error after brief delay
                    Clock.schedule_once(lambda dt: self.dismiss(), 1)
            
            Clock.schedule_once(do_export, 0.1)
            return
            
        except Exception as e:
            Logger.error(f"EditPopup: Error saving image: {e}")
            import traceback
            Logger.error(f"EditPopup: Traceback: {traceback.format_exc()}")
            self.title = f'Error saving: {e}'
    
    def _save_metadata(self, edited_dir, new_name, base_name, version, output_filename):
        """Save metadata JSON file"""
        metadata = {
            'time_of_modification': datetime.now().isoformat(),
            'original_name': base_name,
            'new_name': output_filename,
            'original_path': self.image_path,
            'version': version,
            'user_card_data': self.user_card_data  # Card data from reader (or None)
        }
        
        # Save metadata JSON
        json_filename = f"{new_name}_metadata.json"
        json_path = os.path.join(edited_dir, json_filename)
        with open(json_path, 'w') as f:
            json.dump(metadata, f, indent=2)
        
        Logger.info(f"EditPopup: Saved metadata to {json_path} (user_card_data: {self.user_card_data})")
        return json_path
    
    def _upload_to_server(self, image_path, metadata_path):
        """Upload edited image and metadata to server (runs in background thread)"""
        try:
            import requests
            from get_playlists_v2 import get_auth_instance
            
            # Get authenticated instance
            auth = get_auth_instance()
            if not auth or not auth.is_authenticated():
                Logger.warning("EditPopup: Cannot upload - not authenticated (edited media saved locally only)")
                Logger.warning("EditPopup: Server will NOT receive this edit")
                return False
            
            server_url = auth.auth_data.get('server_url')
            auth_code = auth.auth_data.get('auth_code')
            
            if not server_url or not auth_code:
                Logger.warning("EditPopup: Missing server URL or auth code (upload skipped)")
                return False
            
            # Load metadata from file
            with open(metadata_path, 'r') as meta_file:
                metadata = json.load(meta_file)
            
            # Prepare upload URL - send to the original file endpoint
            upload_url = f"{server_url}/api/player-edit-media"
            headers = {'Authorization': f'Bearer {auth_code}'}
            
            # Add the original filename to metadata so server knows which file was edited
            metadata['original_filename'] = os.path.basename(metadata['original_path'])
            
            # Disable SSL verification for self-signed certificates (like main code does)
            # Note: This is NOT recommended for production with untrusted servers
            Logger.warning("⚠️ SSL verification disabled for edited media upload - only use with trusted servers")
            
            # Prepare file and data for upload
            with open(image_path, 'rb') as img_file:
                files = {
                    'image_file': (metadata['original_filename'], img_file, 'image/jpeg')
                }
                
                # Send metadata as JSON string in form data
                data = {
                    'metadata': json.dumps(metadata),
                    'original_file': metadata['original_filename']
                }
                
                Logger.info(f"EditPopup: 📤 Uploading edited media to {upload_url}")
                Logger.info(f"EditPopup:   - Original file: {metadata['original_filename']}")
                Logger.info(f"EditPopup:   - Edited image: {image_path}")
                Logger.info(f"EditPopup:   - Metadata: {metadata_path}")
                
                try:
                    response = requests.post(upload_url, headers=headers, files=files, data=data, timeout=30, verify=False)
                    
                    if response.status_code == 200:
                        response_data = response.json()
                        Logger.info(f"EditPopup: ✅ Successfully uploaded edited media to server")
                        Logger.info(f"EditPopup: Server response: {response_data}")
                        
                        # DO NOT delete local files - keep them as backup
                        # In case the server doesn't process them, we want to keep the edits locally
                        Logger.info(f"EditPopup: ✓ Keeping local edited files as backup:")
                        Logger.info(f"  - Image: {image_path}")
                        Logger.info(f"  - Metadata: {metadata_path}")
                        
                        # Trigger playlist reload if server provides new version
                        try:
                            new_version = response_data.get('new_playlist_version')
                            if new_version:
                                Logger.info(f"EditPopup: 📡 Server reports new playlist version: {new_version}")
                                Logger.info(f"EditPopup:    Triggering playlist reload on next cycle...")
                                
                                # Playlist reload disabled for now - was causing crashes
                                # Will be re-enabled with better implementation
                                Logger.info(f"EditPopup: ✓ Edited media uploaded successfully")
                        except Exception as e:
                            Logger.warning(f"EditPopup: Could not process playlist version from server: {e}")
                        
                        return True
                    elif response.status_code == 404:
                        Logger.error("EditPopup: ❌ Upload endpoint not found on server (404)")
                        Logger.error("EditPopup:    Server may not support edited media uploads")
                        Logger.error("EditPopup:    Edited media is saved locally only")
                        return False
                    elif response.status_code == 401:
                        Logger.error("EditPopup: ❌ Authentication failed (401) - check auth credentials")
                        Logger.error("EditPopup:    Edited media is saved locally only")
                        return False
                    else:
                        Logger.error(f"EditPopup: ❌ Upload failed with status {response.status_code}")
                        Logger.error(f"EditPopup:    Response: {response.text}")
                        Logger.error("EditPopup:    Edited media is saved locally only")
                        return False
                        
                except requests.exceptions.Timeout:
                    Logger.error("EditPopup: ❌ Upload timed out after 30 seconds")
                    Logger.error("EditPopup:    Check network connection")
                    Logger.error("EditPopup:    Edited media is saved locally only")
                    return False
                except requests.exceptions.ConnectionError as e:
                    Logger.error(f"EditPopup: ❌ Cannot connect to server: {e}")
                    Logger.error("EditPopup:    Check server URL and network connection")
                    Logger.error("EditPopup:    Edited media is saved locally only")
                    return False
                    
        except Exception as e:
            Logger.error(f"EditPopup: ❌ Unexpected error during upload: {e}")
            import traceback
            Logger.error(f"EditPopup: Traceback: {traceback.format_exc()}")
            Logger.error("EditPopup:    Edited media is saved locally only")
            return False
    
    def close_without_saving(self, instance):
        """Close without saving"""
        Logger.info("EditPopup: Closed without saving")
        self.dismiss()
    
    def on_popup_dismiss(self, *args):
        """Resume playback when popup closes - reload current image and continue"""
        # Cancel countdown and auto-close timers
        if self.countdown_event:
            Clock.unschedule(self.countdown_event)
        if self.auto_close_event:
            Clock.unschedule(self.auto_close_event)
        
        # Force remove current widget immediately
        if self.player.current_widget:
            Logger.info("EditPopup: Removing current widget to force reload")
            self.player.ids.content_area.remove_widget(self.player.current_widget)
            self.player.current_widget = None
            Logger.info("EditPopup: ✓ Widget removed, ready for fresh load")
        
        # Resume playback if it wasn't paused before editing
        if not self.was_paused:
            self.player.is_paused = False
            
            # Update button icon to pause (to show it's playing)
            self.player.ids.play_pause_btn.background_normal = self.player.resources_path + '/pause.png'
            self.player.ids.play_pause_btn.background_down = self.player.resources_path + '/pause.png'
            
            # Add delay to ensure file write is complete and synced
            def reload_media(dt):
                Logger.info("EditPopup: ▶ Resuming playback and reloading edited image (force_reload=True)")
                self.player.play_current_media(force_reload=True)
            
            Clock.schedule_once(reload_media, 0.5)
        else:
            Logger.info("EditPopup: Dismissed, keeping paused state")
        
        # Restart control hide timer
        self.player.schedule_hide_controls()
