"""Cavebot-Seite: Wegpunkte-Verwaltung als klarer Workflow.

Links: durchsuch-/filterbare Wegpunkt-Liste. Rechts: Schalter,
Richtungs-Pad und gruppierte Aktionen (statt 24 gleicher Buttons).
Alle Wegpunkt-Typen, Modals und Script-Import/Export bleiben identisch.
"""
import json
from tkinter import BooleanVar, StringVar, filedialog, messagebox, ttk

import customtkinter

from src.importers.bbot import import_bbot
from src.repositories.radar.core import getCoordinate
from src.ui import theme
from src.ui.widgets import Card, SearchableComboBox, positiveInt
from src.utils.core import getScreenshot
from .baseModal import BaseModal
from .refillModal import RefillModal
from .buyBackpackModal import BuyBackpackModal
from .depositItemsModal import DepositItemsModal
from .travelModal import TravelModal
from .refillCheckerModal import RefillCheckerModal
from .suppliesModal import SuppliesModal
from .ignoreCreaturesPage import IgnoreCreaturesPage

ACTION_GROUPS = (
    ('Laufen', (
        ('Walk', 'walk'),
        ('Walk Ignore', ('walk', {'ignore': True})),
        ('Walk Ignore Passinho', ('walk', {'ignore': True, 'passinho': True})),
        ('Single Move', 'singleMove'),
        ('Move Up', 'moveUp'),
        ('Move Down', 'moveDown'),
        ('Use Ladder', 'useLadder'),
        ('RC Direction', 'rightClickDirection'),
        ('Travel', 'travel'),
    )),
    ('Interaktion', (
        ('Open Door', 'openDoor'),
        ('Rope', 'useRope'),
        ('Shovel', 'useShovel'),
        ('Use Right Click', 'rightClickUse'),
        ('Drop Flasks', 'dropFlasks'),
    )),
    ('NPC & Loot', (
        ('Refill', 'refill'),
        ('Refill Checker', 'refillChecker'),
        ('Buy Backpack', 'buyBackpack'),
        ('Deposit Gold', 'depositGold'),
        ('Deposit Items', 'depositItems'),
        ('Deposit House', 'depositItemsHouse'),
    )),
)

DIRECTIONS = (
    ('↑ Nord', 'north'), ('← West', 'west'), ('● Mitte', 'center'),
    ('→ Ost', 'east'), ('↓ Süd', 'south'),
)


class CavebotPage(customtkinter.CTkFrame):
    def __init__(self, master, context):
        super().__init__(master, fg_color='transparent')
        self.context = context
        self.grid_columnconfigure(0, weight=3)
        self.grid_columnconfigure(1, weight=2)
        self.grid_rowconfigure(0, weight=1)

        self.baseModal = None
        self.refillModal = None
        self.backpackModal = None
        self.refillCheckerModal = None
        self.depositItemsModal = None
        self.travelModal = None
        self.suppliesModal = None
        self.ignoreCreaturesPage = None
        self.waypointDirection = StringVar(value='center')

        self._buildListCard()
        self._buildSidePanel()
        self._reloadTable()

    # ------------------------------------------------------------------ Liste
    def _buildListCard(self):
        card = Card(self, 'Wegpunkte', 'Doppelklick zum Bearbeiten')
        card.grid(row=0, column=0, padx=(0, 6), sticky='nsew')
        card.body.grid_rowconfigure(1, weight=1)
        card.body.grid_columnconfigure((0, 1), weight=1)

        self.searchEntry = customtkinter.CTkEntry(
            card.body, placeholder_text='Wegpunkt suchen …',
            font=theme.font(12),
        )
        self.searchEntry.grid(row=0, column=0, sticky='ew', padx=(0, 4))
        self.searchEntry.bind('<KeyRelease>', lambda _e: self._reloadTable())

        types = sorted({spec[1] if isinstance(spec, str) else spec[0]
                        for _group in ACTION_GROUPS for spec in _group[1]})
        self.typeFilter = SearchableComboBox(
            card.body, values=['Alle Typen'] + types,
            placeholder='Typ filtern …',
            command=lambda _v: self._reloadTable(),
        )
        self.typeFilter.grid(row=0, column=1, sticky='ew', padx=(4, 0))
        self.typeFilter.set('Alle Typen')

        tableFrame = customtkinter.CTkFrame(card.body, fg_color=theme.ELEVATED,
                                            corner_radius=10)
        tableFrame.grid(row=1, column=0, columnspan=2, pady=(8, 0),
                        sticky='nsew')
        tableFrame.grid_rowconfigure(0, weight=1)
        tableFrame.grid_columnconfigure(0, weight=1)

        style = ttk.Style()
        style.theme_use('default')
        style.configure(
            'Treeview', background=theme.ELEVATED, foreground=theme.TEXT,
            fieldbackground=theme.ELEVATED, borderwidth=0, rowheight=26,
        )
        style.configure(
            'Treeview.Heading', background=theme.ELEVATED,
            foreground=theme.ACCENT, borderwidth=0,
        )
        style.map('Treeview', background=[('selected', theme.ACCENT)],
                  foreground=[('selected', '#FFFFFF')])

        self.table = ttk.Treeview(
            tableFrame, columns=('label', 'type', 'coordinate', 'options'))
        self.table.grid(row=0, column=0, sticky='nsew')
        self.table.heading('label', text='Label')
        self.table.heading('type', text='Typ')
        self.table.heading('coordinate', text='Koordinate')
        self.table.heading('options', text='Optionen')
        self.table.column('#0', width=0, stretch=False)
        self.table.column('label', width=90)
        self.table.column('type', width=110)
        self.table.column('coordinate', width=110)
        self.table.column('options', width=180)
        self.table.bind('<Double-1>', self.onWaypointDoubleClick)

        footer = customtkinter.CTkFrame(card.body, fg_color='transparent')
        footer.grid(row=2, column=0, columnspan=2, pady=(8, 0), sticky='ew')
        footer.grid_columnconfigure((0, 1, 2), weight=1)
        customtkinter.CTkButton(
            footer, text='Löschen', height=34, corner_radius=8,
            fg_color=theme.ELEVATED, hover_color=theme.DANGER,
            text_color=theme.MUTED, command=self.removeSelectedWaypoints,
        ).grid(row=0, column=0, padx=(0, 4), sticky='ew')
        customtkinter.CTkButton(
            footer, text='Script laden', height=34, corner_radius=8,
            fg_color=theme.ELEVATED, hover_color=theme.BORDER,
            text_color=theme.TEXT, command=self.loadScript,
        ).grid(row=0, column=1, padx=4, sticky='ew')
        customtkinter.CTkButton(
            footer, text='Script speichern', height=34, corner_radius=8,
            fg_color=theme.ELEVATED, hover_color=theme.BORDER,
            text_color=theme.TEXT, command=self.saveScript,
        ).grid(row=0, column=2, padx=(4, 0), sticky='ew')

    # ------------------------------------------------------------------ Seite
    def _buildSidePanel(self):
        side = customtkinter.CTkScrollableFrame(self, fg_color='transparent')
        side.grid(row=0, column=1, padx=(6, 0), sticky='nsew')
        side.grid_columnconfigure(0, weight=1)

        control = Card(side, 'Cavebot')
        control.grid(row=0, column=0, pady=(0, 10), sticky='ew')
        self.enabledSwitch = customtkinter.CTkSwitch(
            control.body, text='Aktiviert', progress_color=theme.ACCENT,
            command=self.onToggleEnabledButton,
        )
        self.enabledSwitch.grid(row=0, column=0, sticky='w', pady=2)
        if self.context.context['ng_cave']['enabled']:
            self.enabledSwitch.select()
        self.runSwitch = customtkinter.CTkSwitch(
            control.body, text='Run to Creatures',
            progress_color=theme.ACCENT, command=self.onToggleRunButton,
        )
        self.runSwitch.grid(row=1, column=0, sticky='w', pady=2)
        if self.context.context['ng_cave']['runToCreatures']:
            self.runSwitch.select()

        self.recordButton = customtkinter.CTkButton(
            control.body, text='Route aufzeichnen', height=32,
            corner_radius=8, font=theme.font(12),
            fg_color=theme.ELEVATED, hover_color=theme.ACCENT_SOFT,
            text_color=theme.TEXT, command=self._toggleRecording,
        )
        self.recordButton.grid(row=2, column=0, sticky='ew', pady=(6, 2))
        self._syncRecordButton()

        direction = Card(side, 'Richtung', 'für den nächsten Wegpunkt')
        direction.grid(row=1, column=0, pady=(0, 10), sticky='ew')
        for index, (label, value) in enumerate(DIRECTIONS):
            button = customtkinter.CTkRadioButton(
                direction.body, text=label, value=value,
                variable=self.waypointDirection, font=theme.font(12),
                fg_color=theme.ACCENT, hover_color=theme.ACCENT_HOVER,
            )
            button.grid(row=index // 2, column=index % 2, sticky='w',
                        pady=2)

        row = 2
        for groupTitle, actions in ACTION_GROUPS:
            group = Card(side, groupTitle)
            group.grid(row=row, column=0, pady=(0, 10), sticky='ew')
            group.body.grid_columnconfigure((0, 1), weight=1)
            for index, (label, spec) in enumerate(actions):
                customtkinter.CTkButton(
                    group.body, text=label, height=32, corner_radius=8,
                    font=theme.font(12), fg_color=theme.ELEVATED,
                    hover_color=theme.ACCENT_SOFT, text_color=theme.TEXT,
                    command=lambda s=spec: self._addAction(s),
                ).grid(row=index // 2, column=index % 2, padx=2, pady=2,
                       sticky='ew')
            row += 1

        sqmCard = Card(side, 'Sperrzonen', 'Koordinaten, die der Bot meidet')
        sqmCard.grid(row=row, column=0, pady=(0, 10), sticky='ew')
        self.sqmListLabel = customtkinter.CTkLabel(
            sqmCard.body, text=self._sqmListText(), font=theme.font(11),
            text_color=theme.MUTED, justify='left', wraplength=220,
        )
        self.sqmListLabel.grid(row=0, column=0, sticky='w', pady=(0, 4))
        sqmButtons = customtkinter.CTkFrame(
            sqmCard.body, fg_color='transparent')
        sqmButtons.grid(row=1, column=0, sticky='ew')
        sqmButtons.grid_columnconfigure((0, 1), weight=1)
        customtkinter.CTkButton(
            sqmButtons, text='Position sperren', height=30, corner_radius=8,
            font=theme.font(11), fg_color=theme.ACCENT,
            hover_color=theme.ACCENT_HOVER, command=self._blockCurrentPosition,
        ).grid(row=0, column=0, padx=(0, 3), sticky='ew')
        customtkinter.CTkButton(
            sqmButtons, text='Letzte löschen', height=30, corner_radius=8,
            font=theme.font(11), fg_color=theme.ELEVATED,
            hover_color=theme.DANGER, text_color=theme.MUTED,
            command=self._removeLastSqm,
        ).grid(row=0, column=1, padx=(3, 0), sticky='ew')
        row += 1

        customtkinter.CTkButton(
            side, text='Vorrats-Wächter', height=34,
            corner_radius=8, font=theme.font(12),
            fg_color=theme.ELEVATED, hover_color=theme.BORDER,
            text_color=theme.TEXT, command=self.openSuppliesModal,
        ).grid(row=row, column=0, pady=(0, 10), sticky='ew')
        row += 1

        sessionCard = Card(
            side, 'Session-Rhythmus',
            'Hunt/Offline mit Zufalls-Jitter, Logout nur in Safe-Zone')
        sessionCard.grid(row=row, column=0, pady=(0, 10), sticky='ew')
        session = self.context.getSessionRhythm()
        self.sessionSwitch = customtkinter.CTkSwitch(
            sessionCard.body, text='Aktiviert', progress_color=theme.ACCENT,
            command=self._saveSession,
        )
        self.sessionSwitch.grid(row=0, column=0, sticky='w', pady=2)
        if session.get('enabled'):
            self.sessionSwitch.select()
        self.sessionHuntEntry = self._sessionRow(
            sessionCard.body, 1, 'Hunt (Minuten)',
            session.get('huntMinutes', 240))
        self.sessionOfflineEntry = self._sessionRow(
            sessionCard.body, 3, 'Offline (Minuten)',
            session.get('offlineMinutes', 30))
        row += 1

        customtkinter.CTkButton(
            side, text='Ignorierte Kreaturen verwalten', height=34,
            corner_radius=8, font=theme.font(12),
            fg_color=theme.ELEVATED, hover_color=theme.BORDER,
            text_color=theme.TEXT, command=self.ignoreCreaturesWindow,
        ).grid(row=row, column=0, pady=(0, 10), sticky='ew')

    # ------------------------------------------------------------------ Tabelle
    def _reloadTable(self):
        needle = self.searchEntry.get().strip().lower() \
            if hasattr(self, 'searchEntry') else ''
        typeFilter = self.typeFilter.get() if hasattr(self, 'typeFilter') \
            else 'Alle Typen'
        self.table.delete(*self.table.get_children())
        for waypoint in self.context.context['ng_cave']['waypoints']['items']:
            haystack = f"{waypoint['label']} {waypoint['type']} {waypoint['coordinate']}".lower()
            if needle and needle not in haystack:
                continue
            if typeFilter != 'Alle Typen' and waypoint['type'] != typeFilter:
                continue
            self.table.insert('', 'end', values=(
                waypoint['label'], waypoint['type'], waypoint['coordinate'],
                waypoint['options'],
            ))

    # ------------------------------------------------------------------ Aktionen
    def _sqmListText(self):
        sqms = self.context.context['ng_cave'].get('specialSqms', [])
        if not sqms:
            return 'Keine Sperrzonen.'
        return '\n'.join(f'{sqm[0]}, {sqm[1]}, {sqm[2]}' for sqm in sqms)

    def _blockCurrentPosition(self):
        coordinate = self.context.context['ng_radar'].get('coordinate')
        if coordinate is None:
            messagebox.showerror(
                'PilotNG', 'Aktuelle Position unbekannt.')
            return
        self.context.addSpecialSqm(coordinate)
        self.sqmListLabel.configure(text=self._sqmListText())

    def _removeLastSqm(self):
        sqms = self.context.context['ng_cave'].get('specialSqms', [])
        if sqms:
            self.context.removeSpecialSqm(len(sqms) - 1)
            self.sqmListLabel.configure(text=self._sqmListText())

    def _addAction(self, spec):
        if isinstance(spec, tuple):
            waypointType, flags = spec
            self.addWaypoint(waypointType, **flags)
        elif spec == 'refill':
            self.openRefillModal()
        elif spec == 'refillChecker':
            self.openRefillCheckerModal()
        elif spec == 'buyBackpack':
            self.openBackpackModal()
        else:
            self.addWaypoint(spec)

    def openBaseModal(self):
        if self.baseModal is None or not self.baseModal.winfo_exists():
            self.baseModal = BaseModal(self, onConfirm=lambda label, options: self.addWaypoint(
                'refill', options))

    def openRefillModal(self):
        if self.refillModal is None or not self.refillModal.winfo_exists():
            self.refillModal = RefillModal(self, onConfirm=lambda label, options: self.addWaypoint(
                'refill', options))

    def openBackpackModal(self):
        if self.backpackModal is None or not self.backpackModal.winfo_exists():
            self.backpackModal = BuyBackpackModal(self, onConfirm=lambda label, options: self.addWaypoint(
                'buyBackpack', options))

    def openRefillCheckerModal(self):
        waypointsLabels = self.context.getAllWaypointLabels()
        if len(waypointsLabels) == 0:
            messagebox.showerror(
                'PilotNG', 'Mindestens ein Wegpunkt braucht ein Label!')
            return
        if self.refillCheckerModal is None or not self.refillCheckerModal.winfo_exists():
            self.refillCheckerModal = RefillCheckerModal(self, onConfirm=lambda label, options: self.addWaypoint(
                'refillChecker', options), waypointsLabels=waypointsLabels)

    def openSuppliesModal(self):
        waypointsLabels = self.context.getAllWaypointLabels()
        if len(waypointsLabels) == 0:
            messagebox.showerror(
                'PilotNG', 'Mindestens ein Wegpunkt braucht ein Label!')
            return
        if self.suppliesModal is None or not self.suppliesModal.winfo_exists():
            self.suppliesModal = SuppliesModal(
                self, self.context, waypointsLabels)

    def _sessionRow(self, parent, row, label, current):
        customtkinter.CTkLabel(
            parent, text=label, font=theme.font(11),
            text_color=theme.MUTED,
        ).grid(row=row, column=0, sticky='w', pady=(4, 0))
        entry = customtkinter.CTkEntry(
            parent, validate='key',
            validatecommand=(self.register(positiveInt), '%P'))
        entry.grid(row=row + 1, column=0, sticky='ew')
        entry.insert(0, str(current))
        entry.bind('<FocusOut>', lambda _e: self._saveSession())
        entry.bind('<Return>', lambda _e: self._saveSession())
        return entry

    def _saveSession(self):
        session = self.context.getSessionRhythm()
        hunt = self.sessionHuntEntry.get()
        offline = self.sessionOfflineEntry.get()
        self.context.setSessionRhythm(
            self.sessionSwitch.get() == 1,
            int(hunt) if hunt.isdigit() else session.get('huntMinutes', 240),
            int(offline) if offline.isdigit() else session.get('offlineMinutes', 30),
        )

    def _toggleRecording(self):
        active, count = self.context.toggleRecording()
        self._syncRecordButton(active)
        if not active:
            self._reloadTable()

    def _syncRecordButton(self, active=None):
        if active is None:
            active = self.context.isRecording()
        self.recordButton.configure(
            text='Aufnahme stoppen' if active else 'Route aufzeichnen',
            fg_color=theme.DANGER if active else theme.ELEVATED,
        )

    # TODO: verificar se a coordenada é walkable
    def addWaypoint(self, waypointType, options=None, ignore=False, passinho=False):
        screenshot = getScreenshot()
        coordinate = getCoordinate(screenshot)

        if coordinate is None:
            messagebox.showerror('PilotNG', 'Die Tibia-Minimap muss sichtbar sein!')
            return

        waypointDirection = self.waypointDirection.get()

        if waypointDirection == 'north':
            coordinate = (coordinate[0], coordinate[1] - 1, coordinate[2])
        elif waypointDirection == 'south':
            coordinate = (coordinate[0], coordinate[1] + 1, coordinate[2])
        elif waypointDirection == 'east':
            coordinate = (coordinate[0] + 1, coordinate[1], coordinate[2])
        elif waypointDirection == 'west':
            coordinate = (coordinate[0] - 1, coordinate[1], coordinate[2])

        if options is None:
            options = {}

        waypoint = {'label': '', 'type': waypointType,
                    'coordinate': coordinate, 'options': options.copy(),
                    'ignore': ignore, 'passinho': passinho}

        if waypointType in ('moveUp', 'moveDown', 'singleMove', 'rightClickDirection'):
            if waypointDirection == 'center':
                messagebox.showerror(
                    'PilotNG',
                    'Dieser Wegpunkt braucht eine Richtung (Nord/West/Ost/Süd).')
                return
            waypoint['options']['direction'] = waypointDirection

        self.context.addWaypoint(waypoint)
        self._reloadTable()

    def removeSelectedWaypoints(self):
        selectedWaypoints = self.table.selection()
        if not selectedWaypoints:
            return
        # Gefilterte Anzeige: Index ueber die echte Liste aufloesen.
        items = self.context.context['ng_cave']['waypoints']['items']
        for row in selectedWaypoints:
            values = self.table.item(row)['values']
            index = next(
                (i for i, waypoint in enumerate(items)
                 if waypoint['label'] == str(values[0])
                 and waypoint['type'] == str(values[1])
                 and str(waypoint['coordinate']) == str(values[2])),
                None,
            )
            if index is not None:
                self.context.removeWaypointByIndex(index)
        self._reloadTable()

    def onWaypointDoubleClick(self, event):
        item = self.table.identify_row(event.y)
        if not item:
            return
        values = self.table.item(item)['values']
        items = self.context.context['ng_cave']['waypoints']['items']
        index = next(
            (i for i, waypoint in enumerate(items)
             if waypoint['label'] == str(values[0])
             and waypoint['type'] == str(values[1])
             and str(waypoint['coordinate']) == str(values[2])),
            None,
        )
        if index is None:
            return
        waypoint = items[index]
        if waypoint['type'] == 'refill':
            if self.refillModal is None or not self.refillModal.winfo_exists():
                self.refillModal = RefillModal(
                    self, waypoint=waypoint, onConfirm=lambda label, options: self.updateWaypointByIndex(index, label=label, options=options))
        elif waypoint['type'] == 'buyBackpack':
            if self.backpackModal is None or not self.backpackModal.winfo_exists():
                self.backpackModal = BuyBackpackModal(
                    self, waypoint=waypoint, onConfirm=lambda label, options: self.updateWaypointByIndex(index, label=label, options=options))
        elif waypoint['type'] == 'depositItems':
            if self.depositItemsModal is None or not self.depositItemsModal.winfo_exists():
                self.depositItemsModal = DepositItemsModal(
                    self, waypoint=waypoint, onConfirm=lambda label, options: self.updateWaypointByIndex(index, label=label, options=options))
        elif waypoint['type'] == 'travel':
            if self.travelModal is None or not self.travelModal.winfo_exists():
                self.travelModal = TravelModal(
                    self, waypoint=waypoint, onConfirm=lambda label, options: self.updateWaypointByIndex(index, label=label, options=options))
        elif waypoint['type'] == 'refillChecker':
            if self.refillCheckerModal is None or not self.refillCheckerModal.winfo_exists():
                waypointsLabels = self.context.getAllWaypointLabels()
                self.refillCheckerModal = RefillCheckerModal(
                    self, waypoint=waypoint, onConfirm=lambda label, options: self.updateWaypointByIndex(index, label=label, options=options), waypointsLabels=waypointsLabels)
        else:
            if self.baseModal is None or not self.baseModal.winfo_exists():
                self.baseModal = BaseModal(
                    self, waypoint=waypoint, onConfirm=lambda label, options: self.updateWaypointByIndex(index, label=label, options=options))

    def updateWaypointByIndex(self, index, label=None, options={}):
        self.context.updateWaypointByIndex(index, label=label, options=options)
        self._reloadTable()

    def onToggleEnabledButton(self):
        self.context.toggleCavebot(self.enabledSwitch.get() == 1)

    def onToggleRunButton(self):
        self.context.toggleRunToCreatures(self.runSwitch.get() == 1)

    def ignoreCreaturesWindow(self):
        if self.ignoreCreaturesPage is None or not self.ignoreCreaturesPage.winfo_exists():
            self.ignoreCreaturesPage = IgnoreCreaturesPage(self.context)
        else:
            self.ignoreCreaturesPage.focus()

    def saveScript(self):
        file = filedialog.asksaveasfilename(
            defaultextension='.pilotscript',
            filetypes=[('PilotNG Script', '*.pilotscript'), ('Alle Dateien', '*.*')],
        )
        if file:
            with open(file, 'w') as f:
                json.dump(self.context.context['ng_cave']['waypoints']['items'], f, indent=4)
            messagebox.showinfo('PilotNG', 'Script gespeichert!')

    def loadScript(self):
        file = filedialog.askopenfilename(
            defaultextension='.pilotscript',
            filetypes=[
                ('PilotNG / BBot Script', '*.pilotscript *.bbot'),
                ('BBot Script', '*.bbot'),
                ('Alle Dateien', '*.*'),
            ],
        )
        if not file:
            return
        try:
            if file.lower().endswith('.bbot'):
                config, report = import_bbot(file, self.context.enabledProfile['config'])
                self.context.importBBot(config)
                script = config['ng_cave']['waypoints']['items']
                if config['ng_cave']['enabled']:
                    self.enabledSwitch.select()
                else:
                    self.enabledSwitch.deselect()
                if config['ng_cave']['runToCreatures']:
                    self.runSwitch.select()
                else:
                    self.runSwitch.deselect()
                details = (
                    f"{report['waypoints']} Wegpunkte, {report['targets']} Ziele, "
                    f"{report['lootItems']} Loot-Items importiert."
                )
                notes = report['warnings'] + report['unsupported']
                if notes:
                    messagebox.showwarning('BBot importiert', details + '\n\n' + '\n'.join(notes))
                else:
                    messagebox.showinfo('BBot importiert', details)
            else:
                with open(file, 'r') as source:
                    script = json.load(source)
                self.context.loadScript(script)
            self._reloadTable()
        except (OSError, ValueError, KeyError, json.JSONDecodeError) as error:
            messagebox.showerror('Import fehlgeschlagen', str(error))
            return
