import json
import os
import subprocess
from pathlib import Path
from tkinter import BooleanVar, messagebox

import customtkinter

from . import theme
from .glass import renderGradient
from .overlay import OverlayWindow
from .widgets import GlassButton
from .pages.account import AccountPage
from .pages.cavebot.cavebotPage import CavebotPage
from .pages.comboSpells import ComboSpellsPage
from .pages.config import ConfigPage
from .pages.dashboard import DashboardPage
from .pages.death import DeathPage
from .pages.healing.healingPage import HealingPage
from .pages.inventory import InventoryPage
from src.security.credentials import load_credentials


class Application(customtkinter.CTk):
    def __init__(self, context):
        super().__init__()
        customtkinter.set_appearance_mode('dark')

        self.context = context
        theme.setTheme(self.context.getUiTheme())
        theme.setMode(self.context.getUiMode())
        theme.setGlassIntensity(self.context.getUiGlass())
        customtkinter.set_appearance_mode(
            'light' if theme.MODE_NAME == 'hell' else 'dark')
        self.overlay = None
        self.title('PilotNG')
        self.geometry('1280x820')
        self.minsize(1080, 700)
        self.configure(fg_color=theme.BG)
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(0, weight=1)

        self.pages = {}
        self.navButtons = {}
        self.currentPage = None
        self.currentPageName = None
        self.pageSpecs = {
            'Dashboard': (DashboardPage, True, theme.ICON_DASHBOARD),
            'Cave Bot': (CavebotPage, True, theme.ICON_CAVEBOT),
            'Healing': (HealingPage, True, theme.ICON_HEALING),
            'Kombos': (ComboSpellsPage, True, theme.ICON_COMBO),
            'Inventar': (InventoryPage, True, theme.ICON_INVENTORY),
            'Tod': (DeathPage, True, theme.ICON_DEATH),
            'Einstellungen': (ConfigPage, True, theme.ICON_CONFIG),
            'Konto': (AccountPage, False, theme.ICON_ACCOUNT),
        }

        self._buildSidebar()
        self._buildWorkspace()
        self._showInitialPage()
        self._showUpdateResult()

    # ------------------------------------------------------------------ Sidebar
    def _buildSidebar(self):
        sidebar = customtkinter.CTkFrame(
            self, width=250, corner_radius=0, fg_color=theme.SIDEBAR,
        )
        sidebar.grid(row=0, column=0, sticky='nsew')
        sidebar.grid_propagate(False)
        sidebar.grid_rowconfigure(len(self.pageSpecs) + 3, weight=1)
        self._sidebar = sidebar
        # Verlauf + Accent-Glow als Hintergrundbild hinter allem.
        self._sidebarBg = customtkinter.CTkLabel(sidebar, text='')
        self._sidebarBg.place(x=0, y=0, relwidth=1, relheight=1)
        self._sidebarBgJob = None
        sidebar.bind('<Configure>', self._scheduleSidebarBg)

        logo_title = customtkinter.CTkLabel(
            sidebar, text='PILOT NG', fg_color=theme.SIDEBAR_TOP,
            font=theme.font(26, 'bold'), text_color=theme.TEXT,
        )
        logo_title.grid(row=0, column=0, padx=24, pady=(30, 2), sticky='w')
        self._logoTitle = logo_title
        logo_sub = customtkinter.CTkLabel(
            sidebar, text='Tibia Automation', fg_color=theme.SIDEBAR_TOP,
            font=theme.font(12), text_color=theme.MUTED,
        )
        logo_sub.grid(row=1, column=0, padx=24, pady=(0, 26), sticky='w')
        self._logoSub = logo_sub

        for row, (name, spec) in enumerate(self.pageSpecs.items(), start=2):
            button = customtkinter.CTkButton(
                sidebar, text=f'  {spec[2]}   {name}', height=48,
                corner_radius=14, anchor='w', font=theme.font(15),
                fg_color=theme.ELEVATED, hover_color=theme.BORDER,
                border_width=1, border_color=theme.GLASS_EDGE,
                text_color=theme.TEXT,
                command=lambda page=name: self.showPage(page),
            )
            button.grid(row=row, column=0, padx=14, pady=3, sticky='ew')
            self.navButtons[name] = button

        bottom = len(self.pageSpecs) + 3
        self.overlayButton = customtkinter.CTkButton(
            sidebar, text=f'{theme.ICON_OVERLAY}  Schnellfenster',
            height=42, corner_radius=12, font=theme.font(13),
            fg_color=theme.ELEVATED, hover_color=theme.BORDER,
            border_width=1, border_color=theme.GLASS_EDGE,
            command=self.toggleOverlay,
        )
        self.overlayButton.grid(
            row=bottom, column=0, padx=14, pady=(6, 4), sticky='ew')

        self.playAllButton = GlassButton(
            sidebar, text=f'{theme.ICON_PLAY}  Alles starten', height=46,
            font=theme.font(14, 'bold'), command=self.startAll,
        )
        self.playAllButton.grid(
            row=bottom + 1, column=0, padx=14, pady=(4, 4), sticky='ew')

        self.updateButton = customtkinter.CTkButton(
            sidebar, text=f'{theme.ICON_UPDATE}  Update installieren',
            height=42, corner_radius=12, font=theme.font(13),
            fg_color=theme.ELEVATED, hover_color=theme.BORDER,
            border_width=1, border_color=theme.GLASS_EDGE,
            text_color=theme.MUTED, command=self.installUpdate,
        )
        self.updateButton.grid(
            row=bottom + 2, column=0, padx=14, pady=(4, 22), sticky='ew')

    def _scheduleSidebarBg(self, _event):
        if self._sidebarBgJob is not None:
            self.after_cancel(self._sidebarBgJob)
        self._sidebarBgJob = self.after(120, self._renderSidebarBg)

    def _renderSidebarBg(self):
        self._sidebarBgJob = None
        width = self._sidebar.winfo_width()
        height = self._sidebar.winfo_height()
        if width < 20 or height < 20:
            return
        image = renderGradient(
            width, height, theme.SIDEBAR_TOP, theme.SIDEBAR_BOTTOM,
            glow=theme.ACCENT, glowAt=(0.15, 1.05), glowRadius=0.9,
            glowAlpha=int(theme.GLOW_ALPHA_BASE * theme.GLASS_INTENSITY / 100))
        self._sidebarBg.configure(image=image)
        self._sidebarBg.image = image  # Referenz gegen GC

    # ------------------------------------------------------------------ Workspace
    def _buildWorkspace(self):
        workspace = customtkinter.CTkFrame(
            self, corner_radius=0, fg_color=theme.BG,
        )
        workspace.grid(row=0, column=1, sticky='nsew')
        workspace.grid_columnconfigure(0, weight=1)
        workspace.grid_rowconfigure(1, weight=1)
        self._workspace = workspace

        header = customtkinter.CTkFrame(
            workspace, height=80, corner_radius=0, fg_color=theme.BG,
        )
        header.grid(row=0, column=0, sticky='ew', padx=28, pady=(12, 0))
        header.grid_columnconfigure(1, weight=1)
        self._header = header

        self.pageTitle = customtkinter.CTkLabel(
            header, text='', font=theme.font(28, 'bold'),
        )
        self.pageTitle.grid(row=0, column=0, sticky='w')

        self.statusLabel = customtkinter.CTkLabel(
            header, text='PAUSIERT', font=theme.font(11, 'bold'),
            text_color=theme.MUTED,
        )
        self.statusLabel.grid(row=0, column=2, padx=(12, 14))

        self.enabledVar = BooleanVar(value=not self.context.context['ng_pause'])
        self.enabledSwitch = customtkinter.CTkSwitch(
            header, text='Bot aktiv', variable=self.enabledVar,
            command=self.onToggleEnabledButton,
            progress_color=theme.ACCENT, button_hover_color=theme.ACCENT_HOVER,
        )
        self.enabledSwitch.grid(row=0, column=3, sticky='e')
        self._refreshStatus()

        self.contentFrame = customtkinter.CTkFrame(
            workspace, corner_radius=16, fg_color=theme.BG,
        )
        self.contentFrame.grid(
            row=1, column=0, padx=26, pady=(4, 24), sticky='nsew',
        )
        self.contentFrame.grid_columnconfigure(0, weight=1)
        self.contentFrame.grid_rowconfigure(0, weight=1)

    # ------------------------------------------------------------------ Seiten
    def _showInitialPage(self):
        try:
            credentialsConfigured = load_credentials() is not None
        except (OSError, ValueError):
            credentialsConfigured = False
        self.showPage('Dashboard' if credentialsConfigured else 'Konto')

    def showPage(self, name, animate=True):
        if self.currentPage is not None:
            self.currentPage.grid_remove()
            self.currentPage.place_forget()
        if name not in self.pages:
            pageClass, needsContext, _glyph = self.pageSpecs[name]
            args = (self.contentFrame, self.context) if needsContext else (self.contentFrame,)
            self.pages[name] = pageClass(*args)
        self.currentPage = self.pages[name]
        self.currentPageName = name
        self.currentPage.grid(row=0, column=0, sticky='nsew')
        self.pageTitle.configure(text=name)
        self._styleNavButtons()
        if animate:
            self._slideIn(self.currentPage)

    def _styleNavButtons(self):
        for pageName, button in self.navButtons.items():
            selected = pageName == self.currentPageName
            button.configure(
                fg_color=theme.ACCENT if selected else theme.ELEVATED,
                hover_color=theme.ACCENT_HOVER if selected else theme.BORDER,
                border_color=theme.ACCENT if selected else theme.GLASS_EDGE,
                text_color='#FFFFFF' if selected else theme.TEXT,
            )

    def _slideIn(self, page, steps=6, distance=42):
        """Seitenwechsel: neue Seite gleitet sanft von rechts herein."""
        page.lift()

        def step(i):
            if page is not self.currentPage:
                return  # weitergeklickt — Animation abbrechen
            if i > steps:
                page.place_forget()
                page.grid(row=0, column=0, sticky='nsew')
                return
            ease = 1 - (1 - i / steps) ** 2  # ease-out
            x = int(distance * (1 - ease))
            page.place(
                in_=self.contentFrame, x=x, y=0,
                relwidth=1.0, relheight=1.0,
            )
            self.after(16, lambda: step(i + 1))
        step(0)

    def applyTheme(self, name):
        """Akzentfarbe wechseln: speichern + alles neu aufbauen."""
        self.context.setUiTheme(name)
        self.applyDesign()

    def applyDesign(self):
        """Modus/Akzent/Glas-Staerke aus dem Profil anwenden."""
        theme.setTheme(self.context.getUiTheme())
        theme.setMode(self.context.getUiMode())
        theme.setGlassIntensity(self.context.getUiGlass())
        customtkinter.set_appearance_mode(
            'light' if theme.MODE_NAME == 'hell' else 'dark')
        for page in self.pages.values():
            page.destroy()
        self.pages = {}
        current = self.currentPageName
        self.currentPage = None
        self._restyleShell()
        if current is not None:
            self.showPage(current, animate=False)

    def _restyleShell(self):
        """Sidebar/Header in den aktuellen Modus-Farben nachziehen."""
        self.configure(fg_color=theme.BG)
        self._workspace.configure(fg_color=theme.BG)
        self._header.configure(fg_color=theme.BG)
        self.contentFrame.configure(fg_color=theme.BG)
        self._sidebar.configure(fg_color=theme.SIDEBAR)
        self._logoTitle.configure(
            fg_color=theme.SIDEBAR_TOP, text_color=theme.TEXT)
        self._logoSub.configure(
            fg_color=theme.SIDEBAR_TOP, text_color=theme.MUTED)
        self._styleNavButtons()
        self.overlayButton.configure(
            fg_color=theme.ELEVATED, hover_color=theme.BORDER,
            border_color=theme.GLASS_EDGE, text_color=theme.TEXT)
        self.updateButton.configure(
            fg_color=theme.ELEVATED, hover_color=theme.BORDER,
            border_color=theme.GLASS_EDGE, text_color=theme.MUTED)
        self.playAllButton._base = theme.ACCENT
        self.playAllButton._hover = theme.ACCENT_HOVER
        self.playAllButton.configure(
            fg_color=theme.ACCENT, hover_color=theme.ACCENT_HOVER)
        self.enabledSwitch.configure(
            progress_color=theme.ACCENT, button_hover_color=theme.ACCENT_HOVER)
        self._refreshStatus()
        self._renderSidebarBg()

    # ------------------------------------------------------------------ Overlay
    def toggleOverlay(self):
        if self.overlay is None or not self.overlay.winfo_exists():
            self.overlay = OverlayWindow(self, self.context)
            return
        if self.overlay.state() == 'withdrawn':
            self.overlay.deiconify()
        else:
            self.overlay.withdraw()

    # ------------------------------------------------------------------ Aktionen
    def onToggleEnabledButton(self):
        if self.enabledVar.get():
            if not self.context.play():
                self.enabledVar.set(False)
        else:
            self.context.pause()
        self._refreshStatus()

    def startAll(self):
        try:
            subprocess.run([
                'schtasks.exe', '/Run', '/TN', 'TibiaPilotNG-OneClick',
            ], check=True, creationflags=subprocess.CREATE_NO_WINDOW)
        except (OSError, subprocess.CalledProcessError):
            messagebox.showerror(
                'PilotNG',
                'Ein-Klick-Start fehlt. Bitte die PilotNG-Einrichtung erneut ausführen.',
                parent=self,
            )
            return
        self.after(300, lambda: os._exit(0))

    def _refreshStatus(self):
        active = self.enabledVar.get()
        self.statusLabel.configure(
            text='AKTIV' if active else 'PAUSIERT',
            text_color=theme.SUCCESS if active else theme.MUTED,
        )

    def installUpdate(self):
        updater = Path.cwd() / 'tools' / 'Update-TibiaPilotNG.ps1'
        if not updater.exists():
            messagebox.showerror('PilotNG', 'Updater wurde nicht gefunden.', parent=self)
            return
        if not messagebox.askyesno(
            'PilotNG Update',
            'PilotNG wird kurz geschlossen, aktualisiert und automatisch neu gestartet.',
            parent=self,
        ):
            return
        subprocess.Popen([
            'powershell.exe', '-NoProfile', '-NonInteractive',
            '-ExecutionPolicy', 'Bypass', '-File', str(updater),
            '-ProcessId', str(os.getpid()),
        ], cwd=Path.cwd())
        self.after(300, lambda: os._exit(0))

    def _showUpdateResult(self):
        resultPath = Path('.update-result.json')
        if not resultPath.exists():
            return
        try:
            result = json.loads(resultPath.read_text(encoding='utf-8-sig'))
            self.after(500, lambda: messagebox.showinfo(
                'PilotNG Update', result.get('message', 'Update abgeschlossen.'),
                parent=self,
            ))
        except (OSError, ValueError):
            pass
        finally:
            try:
                resultPath.unlink()
            except OSError:
                pass
