from pathlib import Path
import importlib.util
import tkinter as tk
from tkinter import ttk


ROOT = Path(__file__).resolve().parents[2]
APP = Path(__file__).resolve().parent
GENERATED = APP / 'generated'
AUTOSAVE = APP / 'autosave.racecore.json'
LIVE_CONFIG = APP / 'live-link-ui.json'
V19_PATH = ROOT / 'outputs' / 'RaceCore_Studio_v1.9' / 'racecore_studio_v19.py'
V19_AUTOSAVE = V19_PATH.parent / 'autosave.racecore.json'

spec = importlib.util.spec_from_file_location('racecore_v19', V19_PATH)
v19 = importlib.util.module_from_spec(spec)
spec.loader.exec_module(v19)

for module in (
    v19, v19.v18, v19.v18.v17, v19.v18.v17.v16,
    v19.v18.v17.v16.v15, v19.v18.v17.v16.v15.v14,
    v19.v18.v17.v16.v15.v14.v13,
    v19.v18.v17.v16.v15.v14.v13.v12,
    v19.v18.v17.v16.v15.v14.v13.v12.v11,
):
    module.APP = APP
    module.GENERATED = GENERATED
    module.AUTOSAVE = AUTOSAVE
    module.LIVE_CONFIG = LIVE_CONFIG

COLORS = v19.COLORS
VALID_POSITIONS = tuple(v19.v18.v17.v16.v15.v14.v13.v12.PLACEMENTS)


def issue(level, title, detail, fix=None):
    return {'level': level, 'title': title, 'detail': detail, 'fix': fix}


def project_checks(studio):
    issues = []
    all_layers = []
    for kind in v19.KINDS:
        for index, raw in enumerate(studio.collection(kind)):
            layer = studio.normalise_item(kind, raw)
            all_layers.append((kind, index, layer))
            name = layer.get('name', '').strip() or f'Laag {index + 1}'
            if kind == 'logo':
                path = Path(layer.get('path', ''))
                if not path.is_file():
                    issues.append(issue(
                        'Fout', 'Logobestand ontbreekt',
                        f'{name}: het gekoppelde PNG/WebP-bestand bestaat niet meer.',
                        ('remove_missing_logo', index),
                    ))
                if layer.get('position') not in VALID_POSITIONS:
                    issues.append(issue(
                        'Fout', 'Onbekende logoplaatsing',
                        f'{name}: de plaatsingszone is niet meer geldig.',
                        ('reset_logo_position', index),
                    ))
                scale = int(layer.get('scale', 55))
                if scale < 10 or scale > 180:
                    issues.append(issue(
                        'Waarschuwing', 'Logoformaat buiten bereik',
                        f'{name}: formaat {scale}% wordt teruggebracht naar het geldige bereik.',
                        ('clamp_logo_scale', index),
                    ))
            else:
                x, y = int(layer.get('centerX', 2048)), int(layer.get('centerY', 2048))
                if not (0 <= x <= 4096 and 0 <= y <= 4096):
                    issues.append(issue(
                        'Fout', 'Onderdeel ligt buiten de textuur',
                        f'{name}: positie X {x}, Y {y} valt buiten het ontwerp.',
                        ('clamp_center', kind, index),
                    ))
            if kind == 'text':
                if not str(layer.get('text', '')).strip():
                    issues.append(issue(
                        'Fout', 'Tekstlaag is leeg', f'{name}: vul tekst in of verwijder de laag.',
                        ('fill_empty_text', index),
                    ))
                size = int(layer.get('fontSize', 180))
                if size < 30 or size > 700:
                    issues.append(issue(
                        'Waarschuwing', 'Tekstformaat buiten bereik',
                        f'{name}: lettergrootte {size} wordt veilig begrensd.',
                        ('clamp_text_size', index),
                    ))
            if kind == 'shape':
                width, height = int(layer.get('width', 900)), int(layer.get('height', 420))
                if width < 20 or height < 20:
                    issues.append(issue(
                        'Waarschuwing', 'Kleurvlak is vrijwel onzichtbaar',
                        f'{name}: breedte en hoogte moeten minimaal 20 pixels zijn.',
                        ('clamp_shape_size', index),
                    ))
            if kind == 'stripe':
                width, length = int(layer.get('width', 70)), int(layer.get('length', 900))
                if width < 4 or length < 40:
                    issues.append(issue(
                        'Waarschuwing', 'Streep is vrijwel onzichtbaar',
                        f'{name}: breedte of lengte is te klein.',
                        ('clamp_stripe_size', index),
                    ))
            opacity = int(layer.get('opacity', 100))
            if opacity < 10 and layer.get('visible', True):
                issues.append(issue(
                    'Tip', 'Zichtbare laag is bijna transparant',
                    f'{name}: dekking staat op slechts {opacity}%.', None,
                ))

    ids = {}
    for kind, index, layer in all_layers:
        layer_id = str(layer.get('id', ''))
        if not layer_id or layer_id in ids:
            issues.append(issue(
                'Waarschuwing', 'Dubbele interne laagcode',
                f"{layer.get('name', 'Naamloze laag')}: RaceCore maakt een unieke code.",
                ('unique_id', kind, index),
            ))
        else:
            ids[layer_id] = (kind, index)

    visible = sum(1 for _kind, _index, layer in all_layers if layer.get('visible', True))
    if not all_layers:
        issues.append(issue(
            'Waarschuwing', 'Ontwerp bevat nog geen eigen lagen',
            'De basiskleur kan worden geëxporteerd, maar er zijn nog geen vlakken, strepen, teksten of logo’s.',
        ))
    elif not visible:
        issues.append(issue(
            'Waarschuwing', 'Alle ontwerplagen zijn verborgen',
            'De export bevat alleen de basiskleur en de beschermde autodelen.',
            ('show_all',),
        ))

    try:
        if (studio.preset.get() != 'Geen strepen' and
                studio.primary.get().upper() == studio.accent.get().upper()):
            issues.append(issue(
                'Tip', 'Presetstrepen hebben dezelfde kleur als de lak',
                'De snelle presetstrepen kunnen hierdoor onzichtbaar lijken.',
            ))
    except (AttributeError, tk.TclError):
        pass

    missing_names = [layer for _kind, _index, layer in all_layers
                     if not layer.get('name', '').strip()]
    if missing_names:
        issues.append(issue(
            'Waarschuwing', 'Naamloze lagen gevonden',
            f'{len(missing_names)} laag/lagen krijgen automatisch een duidelijke naam.',
            ('name_layers',),
        ))
    return issues


class StudioV20(v19.StudioV19):
    def __init__(self):
        migrate = not AUTOSAVE.exists() and V19_AUTOSAVE.exists()
        super().__init__()
        self.title('RaceCore Livery Studio v2.0 - Livery Check')
        if migrate:
            self.load_path(V19_AUTOSAVE, record=False)
            self.autosave()

    def build_layout(self):
        super().build_layout()
        header = self.winfo_children()[0]
        for child in header.winfo_children():
            if isinstance(child, tk.Label) and 'Livery Studio' in child.cget('text'):
                child.configure(text='Livery Studio  v2.0')
        toolbar = self.winfo_children()[1]
        for child in toolbar.winfo_children():
            if isinstance(child, tk.Button):
                child.configure(font=('Segoe UI', 8, 'bold'), padx=4)
        tk.Button(
            toolbar, text='Livery Check', command=self.livery_check_dialog,
            font=('Segoe UI', 8, 'bold'), fg=COLORS.NAVY, bg='#FCA5A5',
            activebackground='#FECACA', relief='flat', padx=6,
        ).pack(side='left', padx=(0, 5), pady=6)
        self.organise_toolbar(toolbar)
        self.install_preset_help()

    def organise_toolbar(self, toolbar):
        feature_buttons = {
            'Sponsor / Logo', 'Strepen / lijnen', 'Vormen / kleurvlakken',
            'Startontwerp', 'Logo bibliotheek', 'Tekst / nr.', 'Sponsorplan',
            'Laagoverzicht', 'Livery Check',
        }
        for child in toolbar.winfo_children():
            if isinstance(child, tk.Button) and child.cget('text') in feature_buttons:
                child.pack_forget()
        for child in toolbar.winfo_children():
            if isinstance(child, tk.Button):
                child.configure(font=('Segoe UI', 9, 'bold'), padx=10)

        menus = [
            ('ONTWERP', '#A7F3D0', [
                ('Startontwerp kiezen', self.template_wizard),
                ('Kleurvlakken beheren', self.shape_dialog),
                ('Strepen en lijnen beheren', self.stripe_dialog),
            ]),
            ('LOGO & TEKST', '#F9A8D4', [
                ('Logo-bibliotheek', self.asset_library_dialog),
                ('Logo-lagen beheren', self.logo_dialog),
                ('Tekst en startnummers', self.text_dialog),
                ('Slim sponsorplan', self.sponsor_planner),
            ]),
            ('BEHEREN', '#93C5FD', [
                ('Centraal laagoverzicht', self.design_layers_dialog),
                ('Livery Check & export', self.livery_check_dialog),
            ]),
        ]
        self.toolbar_menus = []
        for title, colour, entries in menus:
            button = tk.Menubutton(
                toolbar, text=f'{title}  ▾', font=('Segoe UI', 9, 'bold'),
                fg=COLORS.NAVY, bg=colour, activebackground=colour,
                activeforeground=COLORS.NAVY, relief='flat', padx=12,
                cursor='hand2',
            )
            menu = tk.Menu(
                button, tearoff=False, font=('Segoe UI', 10),
                bg='#F8FAFC', fg='#0F172A', activebackground='#DBEAFE',
                activeforeground='#0F172A', relief='flat', bd=1,
            )
            for label, command in entries:
                menu.add_command(label=label, command=command)
            button.configure(menu=menu)
            button.pack(side='left', padx=(7, 0), pady=6)
            self.toolbar_menus.append((button, menu))

    def install_preset_help(self):
        help_label = None
        stack = list(self.winfo_children())
        while stack:
            widget = stack.pop()
            try:
                stack.extend(widget.winfo_children())
                if (isinstance(widget, tk.Label) and
                        str(widget.cget('text')).startswith('Clean Center voorkomt')):
                    help_label = widget
                    break
            except tk.TclError:
                continue
        if not help_label:
            return
        self.preset_help = tk.StringVar()
        help_label.configure(textvariable=self.preset_help)
        descriptions = {
            'Geen strepen': 'Geen automatische presetlijnen. Voeg zelf vlakken en strepen toe via Ontwerp.',
            'Clean Center': 'Een rustige centrale lijn zonder losse UV-fragmenten rond spiegel en spatbord.',
            'Center + Sills': 'Centrale lijnen met extra accenten langs de onderzijde van de auto.',
            'Full Seam Proof': 'Toont alle technische presetlijnen; vooral geschikt als controleweergave.',
        }

        def update_help(*_args):
            self.preset_help.set(descriptions.get(
                self.preset.get(), 'Kies een preset of bouw het ontwerp volledig met eigen lagen.'
            ))

        self.preset.trace_add('write', update_help)
        update_help()

    def livery_check_dialog(self):
        win = tk.Toplevel(self)
        win.title('RaceCore Livery Check & Export Assistant')
        win.geometry('1020x680')
        win.minsize(880, 590)
        win.configure(bg=COLORS.PANEL)
        win.transient(self)
        win.grab_set()

        top = tk.Frame(win, bg=COLORS.PANEL)
        top.pack(fill='x', padx=24, pady=(18, 10))
        tk.Label(top, text='LIVERY CHECK', font=('Segoe UI', 16, 'bold'),
                 fg=COLORS.CYAN, bg=COLORS.PANEL).pack(side='left')
        self.check_summary = tk.StringVar()
        tk.Label(top, textvariable=self.check_summary, font=('Segoe UI', 10, 'bold'),
                 fg=COLORS.TEXT, bg=COLORS.PANEL).pack(side='right')

        explainer = tk.Frame(win, bg='#101F3C')
        explainer.pack(fill='x', padx=24, pady=(0, 10))
        tk.Label(
            explainer,
            text='Rood = eerst oplossen   |   Oranje = controleren   |   Blauw = ontwerptip',
            font=('Segoe UI', 9, 'bold'), fg='#DBEAFE', bg='#101F3C',
        ).pack(anchor='w', padx=14, pady=9)

        frame = tk.Frame(win, bg=COLORS.PANEL)
        frame.pack(fill='both', expand=True, padx=24)
        self.check_tree = ttk.Treeview(
            frame, columns=('level', 'detail'), show='headings', selectmode='browse',
        )
        self.check_tree.heading('level', text='Niveau')
        self.check_tree.heading('detail', text='Controle en uitleg')
        self.check_tree.column('level', width=115, minwidth=100, anchor='center')
        self.check_tree.column('detail', width=760, minwidth=420)
        scroll = ttk.Scrollbar(frame, orient='vertical', command=self.check_tree.yview)
        self.check_tree.configure(yscrollcommand=scroll.set)
        self.check_tree.pack(side='left', fill='both', expand=True)
        scroll.pack(side='right', fill='y')
        self.check_tree.tag_configure('Fout', foreground='#FCA5A5')
        self.check_tree.tag_configure('Waarschuwing', foreground='#FBBF24')
        self.check_tree.tag_configure('Tip', foreground='#93C5FD')
        self.check_tree.tag_configure('Klaar', foreground='#86EFAC')

        buttons = tk.Frame(win, bg=COLORS.PANEL)
        buttons.pack(fill='x', padx=24, pady=16)
        tk.Button(buttons, text='VEILIG AUTOMATISCH HERSTELLEN',
                  command=self.apply_safe_fixes, font=('Segoe UI', 9, 'bold'),
                  fg=COLORS.NAVY, bg='#FDE68A', relief='flat', pady=10).pack(
                      side='left', fill='x', expand=True, padx=(0, 5))
        tk.Button(buttons, text='PREVIEW OPNIEUW OPBOUWEN',
                  command=lambda: self.generate(False), font=('Segoe UI', 9, 'bold'),
                  fg=COLORS.TEXT, bg='#334155', relief='flat', pady=10).pack(
                      side='left', fill='x', expand=True, padx=5)
        self.check_export_button = tk.Button(
            buttons, text='GOEDGEKEURD → APPLY TO LMU',
            command=self.checked_apply_to_lmu, font=('Segoe UI', 9, 'bold'),
            fg=COLORS.NAVY, bg='#86EFAC', relief='flat', pady=10,
        )
        self.check_export_button.pack(side='left', fill='x', expand=True, padx=(5, 0))
        tk.Button(win, text='SLUITEN', command=win.destroy,
                  font=('Segoe UI', 10, 'bold'), fg=COLORS.TEXT,
                  bg='#475569', relief='flat', pady=9).pack(fill='x', padx=24,
                                                           pady=(0, 16))
        self.refresh_livery_check()

    def refresh_livery_check(self):
        if not self.live_widget(self, 'check_tree'):
            return
        self.current_checks = project_checks(self)
        self.check_tree.delete(*self.check_tree.get_children())
        counts = {'Fout': 0, 'Waarschuwing': 0, 'Tip': 0}
        for index, check in enumerate(self.current_checks):
            counts[check['level']] += 1
            detail = f"{check['title']} — {check['detail']}"
            self.check_tree.insert('', 'end', iid=f'check:{index}',
                                   values=(check['level'], detail),
                                   tags=(check['level'],))
        if not self.current_checks:
            self.check_tree.insert('', 'end', values=('Klaar',
                'Geen technische problemen gevonden. De livery is klaar voor LMU.'),
                tags=('Klaar',))
        self.check_summary.set(
            f"{counts['Fout']} fouten | {counts['Waarschuwing']} waarschuwingen | "
            f"{counts['Tip']} tips"
        )
        if counts['Fout']:
            self.check_export_button.configure(state='disabled', bg='#64748B',
                                               text='LOS EERST DE RODE MELDINGEN OP')
        else:
            self.check_export_button.configure(state='normal', bg='#86EFAC',
                                               text='GOEDGEKEURD → APPLY TO LMU')

    def apply_safe_fixes(self):
        fixes = [check['fix'] for check in project_checks(self) if check.get('fix')]
        if not fixes:
            self.status.set('Geen automatische reparaties nodig')
            self.refresh_livery_check()
            return
        removed_logo_indexes = sorted(
            [fix[1] for fix in fixes if fix[0] == 'remove_missing_logo'], reverse=True
        )
        for fix in fixes:
            action = fix[0]
            if action == 'remove_missing_logo':
                continue
            if action == 'reset_logo_position' and fix[1] < len(self.logo_layers):
                self.logo_layers[fix[1]]['position'] = 'Roof / engine cover'
            elif action == 'clamp_logo_scale' and fix[1] < len(self.logo_layers):
                layer = self.logo_layers[fix[1]]
                layer['scale'] = max(10, min(180, int(layer.get('scale', 55))))
            elif action == 'clamp_center':
                _action, kind, index = fix
                collection = self.collection(kind)
                if index < len(collection):
                    layer = collection[index]
                    layer['centerX'] = max(0, min(4096, int(layer.get('centerX', 2048))))
                    layer['centerY'] = max(0, min(4096, int(layer.get('centerY', 2048))))
            elif action == 'fill_empty_text' and fix[1] < len(self.text_layers):
                self.text_layers[fix[1]]['text'] = 'TEKST'
            elif action == 'clamp_text_size' and fix[1] < len(self.text_layers):
                layer = self.text_layers[fix[1]]
                layer['fontSize'] = max(30, min(700, int(layer.get('fontSize', 180))))
            elif action == 'clamp_shape_size' and fix[1] < len(self.shape_layers):
                layer = self.shape_layers[fix[1]]
                layer['width'] = max(20, int(layer.get('width', 900)))
                layer['height'] = max(20, int(layer.get('height', 420)))
            elif action == 'clamp_stripe_size' and fix[1] < len(self.stripe_layers):
                layer = self.stripe_layers[fix[1]]
                layer['width'] = max(4, int(layer.get('width', 70)))
                layer['length'] = max(40, int(layer.get('length', 900)))
            elif action == 'unique_id':
                _action, kind, index = fix
                collection = self.collection(kind)
                if index < len(collection):
                    collection[index]['id'] = f'{kind}-{index + 1}-fixed'
            elif action == 'show_all':
                for kind in v19.KINDS:
                    for layer in self.collection(kind):
                        layer['visible'] = True
            elif action == 'name_layers':
                for kind in v19.KINDS:
                    _group, type_name = v19.GROUP_INFO[kind]
                    for index, layer in enumerate(self.collection(kind)):
                        if not layer.get('name', '').strip():
                            layer['name'] = f'{type_name} {index + 1}'
        for index in removed_logo_indexes:
            if 0 <= index < len(self.logo_layers):
                self.logo_layers.pop(index)
        self.changed()
        self.refresh_design_layers()
        self.status.set(f'{len(fixes)} veilige reparaties uitgevoerd')
        self.refresh_livery_check()

    def checked_apply_to_lmu(self):
        checks = project_checks(self)
        errors = [check for check in checks if check['level'] == 'Fout']
        if errors:
            self.status.set('Los eerst de rode meldingen op')
            self.refresh_livery_check()
            return
        self.generate(True)


if __name__ == '__main__':
    StudioV20().mainloop()
