#!/usr/bin/env python3
import sys
import time
import requests
from io import BytesIO
from PIL import Image
from api.auth import get_spotify_client
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.text import Text
from rich import box
from rich.align import Align
from rich.live import Live
from rich.layout import Layout
from rich.progress import Progress, BarColumn, TextColumn


class SpotifyRichApp:
    def __init__(self):
        self.console = Console()
        self.spotify_client = None
        self.playlists = []
        self.current_view = "main"  # "main" or "playlist"
        self.selected_playlist_index = 0
        self.selected_track_index = 0
        self.current_playlist = None
        self.current_tracks = []
        self.fixed_height = 15
        self.visible_items = 12  # Number of items visible at once
        self.playlist_scroll_offset = 0
        self.track_scroll_offset = 0
        self.now_playing = None
        self.last_update_time = 0
        self.cached_album_art = []
        
    def get_key(self, timeout=0.1):
        """Get a single key press with timeout"""
        try:
            import termios
            import tty
            import select
            
            fd = sys.stdin.fileno()
            old_settings = termios.tcgetattr(fd)
            try:
                tty.setcbreak(fd)  # Use cbreak instead of raw mode
                
                # Check if input is available within timeout
                if select.select([sys.stdin], [], [], timeout)[0]:
                    ch = sys.stdin.read(1)
                    
                    # Handle arrow keys (escape sequences)
                    if ch == '\x1b':
                        # Try to read the rest of the escape sequence
                        extra = ""
                        if select.select([sys.stdin], [], [], 0.01)[0]:
                            extra += sys.stdin.read(1)
                            if select.select([sys.stdin], [], [], 0.01)[0]:
                                extra += sys.stdin.read(1)
                        ch += extra
                            
                    return ch
                else:
                    return None  # No key pressed within timeout
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        except ImportError:
            # Windows fallback
            try:
                import msvcrt
                if msvcrt.kbhit():
                    return msvcrt.getch().decode('utf-8')
                else:
                    time.sleep(timeout)
                    return None
            except ImportError:
                return input()[:1]

    def format_duration(self, ms):
        """Convert milliseconds to MM:SS format"""
        seconds = ms // 1000
        minutes = seconds // 60
        seconds = seconds % 60
        return f"{minutes}:{seconds:02d}"

    def get_album_art_ascii(self, image_url, max_width=25, max_height=12):
        """Get album art as ASCII art"""
        if not image_url:
            return []
            
        try:
            # Download image
            response = requests.get(image_url, timeout=5)
            img = Image.open(BytesIO(response.content))
            
            # Convert to grayscale and resize
            img = img.convert('L')
            img = img.resize((max_width, max_height), Image.Resampling.LANCZOS)
            
            # ASCII characters from dark to light
            ascii_chars = " .:-=+*#%@"
            pixels = img.load()
            
            ascii_art = []
            for y in range(max_height):
                row = ""
                for x in range(max_width):
                    brightness = pixels[x, y]
                    char_index = int((brightness / 255) * (len(ascii_chars) - 1))
                    row += ascii_chars[char_index]
                ascii_art.append(row)
            
            return ascii_art
        except Exception:
            return []

    def create_playlist_view(self):
        """Create the main playlist selection view with scrolling"""
        width = self.console.width
        
        # Header
        header_text = Text("🎵 Spotify Rich - Your Playlists", style="bold magenta", justify="center")
        header_panel = Panel(header_text, box=box.DOUBLE_EDGE)
        
        # Calculate visible range
        start_idx = self.playlist_scroll_offset
        end_idx = min(start_idx + self.visible_items, len(self.playlists))
        visible_playlists = self.playlists[start_idx:end_idx]
        
        # Playlist table
        playlist_table = Table(show_header=True, box=None, padding=(0, 2))
        playlist_table.add_column("", width=3)  # Selection indicator
        playlist_table.add_column("Playlist", style="white", overflow="ellipsis")
        playlist_table.add_column("Tracks", style="green", justify="center", width=8)
        playlist_table.add_column("Public", style="yellow", justify="center", width=8)
        
        for i, playlist in enumerate(visible_playlists):
            actual_index = start_idx + i
            indicator = "►" if actual_index == self.selected_playlist_index else " "
            name = playlist.get('name', 'N/A')
            track_count = str(playlist.get('tracks', {}).get('total', 0))
            is_public = "Yes" if playlist.get('public', False) else "No"
            
            style = "bold cyan" if actual_index == self.selected_playlist_index else "white"
            playlist_table.add_row(indicator, name, track_count, is_public, style=style)
        
        # Add empty rows to reach fixed height
        current_rows = len(visible_playlists)
        empty_rows_needed = max(0, self.visible_items - current_rows)
        for _ in range(empty_rows_needed):
            playlist_table.add_row(" ", " ", " ", " ")
        
        # Show scroll indicators if needed
        scroll_info = ""
        if len(self.playlists) > self.visible_items:
            current_page = (self.selected_playlist_index // self.visible_items) + 1
            total_pages = (len(self.playlists) + self.visible_items - 1) // self.visible_items
            scroll_info = f" - Page {current_page}/{total_pages}"
        
        content_panel = Panel(
            playlist_table,
            title=f"[bold green]Your Playlists ({self.selected_playlist_index + 1}/{len(self.playlists)}){scroll_info}[/bold green]",
            border_style="green",
            box=box.ROUNDED if width > 60 else box.SQUARE,
            height=self.fixed_height
        )
        
        # Footer
        footer_text = Text("↑↓: Navigate | Enter: View Tracks | q/Esc: Quit", 
                          style="dim", justify="center")
        footer_panel = Panel(footer_text, box=box.SQUARE)
        
        return header_panel, content_panel, footer_panel

    def create_track_view(self):
        """Create the track view for selected playlist with scrolling"""
        width = self.console.width
        
        # Header
        playlist_name = self.current_playlist.get('name', 'Unknown Playlist')
        header_text = Text(f"🎵 {playlist_name}", style="bold magenta", justify="center")
        header_panel = Panel(header_text, box=box.DOUBLE_EDGE)
        
        # Filter out invalid tracks
        valid_tracks = [track_item for track_item in self.current_tracks 
                       if track_item and track_item.get('track')]
        
        # Calculate visible range
        start_idx = self.track_scroll_offset
        end_idx = min(start_idx + self.visible_items, len(valid_tracks))
        visible_tracks = valid_tracks[start_idx:end_idx]
        
        # Track table
        track_table = Table(show_header=True, box=None, padding=(0, 1))
        track_table.add_column("", width=3)  # Selection indicator
        track_table.add_column("#", width=4, justify="right")
        track_table.add_column("Track", style="white", overflow="ellipsis")
        track_table.add_column("Artist", style="cyan", overflow="ellipsis")
        track_table.add_column("Duration", style="green", justify="center", width=8)
        
        for i, track_item in enumerate(visible_tracks):
            actual_index = start_idx + i
            track = track_item['track']
            indicator = "►" if actual_index == self.selected_track_index else " "
            track_num = str(actual_index + 1)
            name = track.get('name', 'Unknown')
            artists = ', '.join([artist['name'] for artist in track.get('artists', [])])
            duration_ms = track.get('duration_ms', 0)
            duration = self.format_duration(duration_ms)
            
            style = "bold cyan" if actual_index == self.selected_track_index else "white"
            track_table.add_row(indicator, track_num, name, artists, duration, style=style)
        
        # Add empty rows to reach fixed height
        current_rows = len(visible_tracks)
        empty_rows_needed = max(0, self.visible_items - current_rows)
        for _ in range(empty_rows_needed):
            track_table.add_row(" ", " ", " ", " ", " ")
        
        # Show scroll indicators if needed
        scroll_info = ""
        if len(valid_tracks) > self.visible_items:
            current_page = (self.selected_track_index // self.visible_items) + 1
            total_pages = (len(valid_tracks) + self.visible_items - 1) // self.visible_items
            scroll_info = f" - Page {current_page}/{total_pages}"
        
        content_panel = Panel(
            track_table,
            title=f"[bold green]Tracks ({self.selected_track_index + 1}/{len(valid_tracks)}){scroll_info}[/bold green]",
            border_style="green",
            box=box.ROUNDED if width > 60 else box.SQUARE,
            height=self.fixed_height
        )
        
        # Footer
        footer_text = Text("↑↓: Navigate | Backspace: Back to Playlists | q/Esc: Quit", 
                          style="dim", justify="center")
        footer_panel = Panel(footer_text, box=box.SQUARE)
        
        return header_panel, content_panel, footer_panel

    def create_now_playing_panel(self):
        """Create the now playing panel with album art"""
        if not self.now_playing or not self.now_playing.get('item'):
            # No track playing
            content = Align.center(
                Text("No track playing", style="dim italic"),
                vertical="middle"
            )
        else:
            track = self.now_playing['item']
            is_playing = self.now_playing.get('is_playing', False)
            
            # Create now playing info
            track_name = track.get('name', 'Unknown Track')
            artists = ', '.join([artist['name'] for artist in track.get('artists', [])])
            album = track.get('album', {}).get('name', 'Unknown Album')
            
            # Progress info with real-time estimation
            progress_ms = self.now_playing.get('progress_ms', 0)
            duration_ms = track.get('duration_ms', 0)
            
            # Estimate current progress
            time_elapsed = (time.time() - self.last_update_time) * 1000
            estimated_progress = progress_ms + (time_elapsed if is_playing else 0)
            estimated_progress = min(estimated_progress, duration_ms)
            
            progress_text = f"{self.format_duration(int(estimated_progress))} / {self.format_duration(duration_ms)}"
            
            # Status icon
            status_icon = "▶️" if is_playing else "⏸️"
            
            # Create content with album art
            content_parts = []
            
            # Album art (if available)
            if self.cached_album_art:
                art_text = Text("\n".join(self.cached_album_art), style="white")
                content_parts.append(art_text)
                content_parts.append(Text("\n"))
            
            # Track info
            info_table = Table(show_header=False, box=None, padding=(0, 1))
            info_table.add_column("", style="dim", width=8)
            info_table.add_column("", style="white")
            
            info_table.add_row("Status:", f"{status_icon} {'Playing' if is_playing else 'Paused'}")
            info_table.add_row("Track:", track_name[:30] + "..." if len(track_name) > 30 else track_name)
            info_table.add_row("Artist:", artists[:30] + "..." if len(artists) > 30 else artists)
            info_table.add_row("Album:", album[:30] + "..." if len(album) > 30 else album)
            
            # Progress bar
            if duration_ms > 0:
                progress_pct = estimated_progress / duration_ms
                bar_width = 20
                filled = int(bar_width * progress_pct)
                empty = bar_width - filled
                progress_bar = "█" * filled + "░" * empty
                info_table.add_row("Progress:", progress_bar)
            
            info_table.add_row("Time:", progress_text)
            
            if self.cached_album_art:
                from rich.console import Group
                content = Group(*content_parts, info_table)
            else:
                content = info_table
        
        return Panel(
            content,
            title="[bold magenta]🎵 Now Playing[/bold magenta]",
            border_style="magenta",
            box=box.ROUNDED,
            height=self.fixed_height
        )

    def create_layout(self):
        """Create complete layout for current view with now playing panel"""
        # Create main layout
        layout = Layout()
        
        # Split main layout: left side for content, right side for now playing
        layout.split_row(
            Layout(name="main", ratio=3),
            Layout(name="now_playing", ratio=1, minimum_size=35)
        )
        
        # Get main content
        if self.current_view == "main":
            header, content, footer = self.create_playlist_view()
        elif self.current_view == "playlist":
            header, content, footer = self.create_track_view()
        
        # Create main content layout
        main_layout = Layout()
        main_layout.split_column(
            Layout(header, size=3),
            Layout("", size=1),  # Spacer
            Layout(content),
            Layout("", size=1),  # Spacer
            Layout(footer, size=3)
        )
        
        # Add to main layout
        layout["main"].update(main_layout)
        layout["now_playing"].update(self.create_now_playing_panel())
        
        return layout

    def handle_playlist_input(self, key):
        """Handle input for playlist view with scrolling"""
        if key == '\x1b[A':  # Up arrow
            if self.selected_playlist_index > 0:
                self.selected_playlist_index -= 1
                # Update scroll offset if needed
                if self.selected_playlist_index < self.playlist_scroll_offset:
                    self.playlist_scroll_offset = self.selected_playlist_index
        elif key == '\x1b[B':  # Down arrow
            if self.selected_playlist_index < len(self.playlists) - 1:
                self.selected_playlist_index += 1
                # Update scroll offset if needed
                if self.selected_playlist_index >= self.playlist_scroll_offset + self.visible_items:
                    self.playlist_scroll_offset = self.selected_playlist_index - self.visible_items + 1
        elif key in ['\r', '\n']:  # Enter
            self.load_playlist_tracks()
        elif key in ['q', 'Q', '\x1b', '\x03']:  # q, Escape, or Ctrl+C
            return False
        return True

    def handle_track_input(self, key):
        """Handle input for track view with scrolling"""
        # Filter out invalid tracks for proper counting
        valid_tracks = [track for track in self.current_tracks if track and track.get('track')]
        
        if key == '\x1b[A':  # Up arrow
            if self.selected_track_index > 0:
                self.selected_track_index -= 1
                # Update scroll offset if needed
                if self.selected_track_index < self.track_scroll_offset:
                    self.track_scroll_offset = self.selected_track_index
        elif key == '\x1b[B':  # Down arrow
            if self.selected_track_index < len(valid_tracks) - 1:
                self.selected_track_index += 1
                # Update scroll offset if needed
                if self.selected_track_index >= self.track_scroll_offset + self.visible_items:
                    self.track_scroll_offset = self.selected_track_index - self.visible_items + 1
        elif key in ['\x7f', '\x08']:  # Backspace or Delete
            self.current_view = "main"
            self.current_playlist = None
            self.current_tracks = []
            self.selected_track_index = 0
            self.track_scroll_offset = 0
        elif key in ['q', 'Q', '\x1b', '\x03']:  # q, Escape, or Ctrl+C
            return False
        return True

    def load_playlist_tracks(self):
        """Load tracks for the selected playlist"""
        if not self.playlists:
            return
            
        selected_playlist = self.playlists[self.selected_playlist_index]
        self.current_playlist = selected_playlist
        self.current_view = "playlist"
        self.selected_track_index = 0
        self.track_scroll_offset = 0  # Reset scroll when entering new playlist
        
        try:
            playlist_id = selected_playlist['id']
            tracks_data = self.spotify_client.playlist_tracks(playlist_id)
            self.current_tracks = tracks_data.get('items', [])
        except Exception as e:
            self.console.print(f"[red]Error loading tracks: {e}[/red]")
            self.current_tracks = []

    def update_now_playing(self):
        """Update the now playing information and album art"""
        try:
            current_playback = self.spotify_client.current_playback()
            if current_playback and current_playback.get('item'):
                track = current_playback['item']
                track_id = track.get('id')
                
                # Check if track changed to update album art
                if not self.now_playing or self.now_playing.get('item', {}).get('id') != track_id:
                    # Get album art
                    album_images = track.get('album', {}).get('images', [])
                    if album_images:
                        # Use smallest image for faster loading
                        album_art_url = album_images[-1]['url']
                        self.cached_album_art = self.get_album_art_ascii(album_art_url)
                
                self.now_playing = current_playback
            else:
                self.now_playing = None
                self.cached_album_art = []
        except Exception:
            self.now_playing = None
            self.cached_album_art = []

    def get_user_playlists(self):
        """Get all user playlists with pagination"""
        playlists = []
        results = self.spotify_client.current_user_playlists(limit=50)
        
        while results:
            playlists.extend(results['items'])
            if results['next']:
                results = self.spotify_client.next(results)
            else:
                break
                
        return playlists

    def initialize_spotify(self):
        """Initialize Spotify client and load playlists"""
        try:
            self.console.print("[bold green]🎵 Connecting to Spotify...[/bold green]")
            
            self.spotify_client = get_spotify_client()
            self.playlists = self.get_user_playlists()
            
            # Load initial now playing info
            self.update_now_playing()
            
            if not self.playlists:
                self.console.print("[yellow]No playlists found![/yellow]")
                return False
                
            return True
            
        except ValueError as e:
            self.console.print(f"[bold red]❌ Configuration Error:[/bold red] {str(e)}")
            self.console.print("\n[yellow]Please ensure you have a .env file with:[/yellow]")
            self.console.print("  SPOTIFY_CLIENT_ID=your_client_id")
            self.console.print("  SPOTIFY_CLIENT_SECRET=your_client_secret")
            return False
        except Exception as e:
            self.console.print(f"[bold red]❌ Error:[/bold red] {str(e)}")
            return False

    def run(self):
        """Main application loop with smooth updates"""
        if not self.initialize_spotify():
            return
        
        try:
            with Live(self.create_layout(), console=self.console, refresh_per_second=1, auto_refresh=False) as live:
                while True:
                    key = self.get_key(timeout=0.1)  # Non-blocking with 100ms timeout
                    needs_update = False
                    
                    # Only handle key input if a key was actually pressed
                    if key is not None:
                        # Debug: log the key (temporarily)
                        self.console.log(f"Key pressed: {repr(key)}")
                        
                        continue_running = True
                        if self.current_view == "main":
                            continue_running = self.handle_playlist_input(key)
                        elif self.current_view == "playlist":
                            continue_running = self.handle_track_input(key)
                        
                        if not continue_running:
                            break
                        
                        needs_update = True
                    
                    # Update now playing info every second
                    current_time = time.time()
                    if current_time - self.last_update_time >= 1.0:
                        self.update_now_playing()
                        self.last_update_time = current_time
                        needs_update = True
                    
                    # Only update display when something actually changed
                    if needs_update:
                        live.update(self.create_layout())
                        live.refresh()
                        
        except KeyboardInterrupt:
            pass
        
        self.console.print("\n[yellow]👋 Goodbye![/yellow]")


if __name__ == "__main__":
    try:
        app = SpotifyRichApp()
        app.run()
    except KeyboardInterrupt:
        print("\n\nInterrupted by user")
    except Exception as e:
        print(f"\n\nError: {str(e)}")
        print("Make sure you have the required dependencies installed:")
        print("pip install spotipy python-dotenv rich pillow requests")