#!/usr/bin/env python3
# this_file: src/qtuidoctools/__main__.py
"""
qtuidoctools
------------
Copyright (c) 2019 Adam Twardoch <adam+github@twardoch.com>
MIT license. Python 3.11+.

qtuidoctools update -d PATH_TO_PROTEUS_REPO -t helptips.yaml -o yaml
"""

import sys

import fire
from yaplon import oyaml

from . import __version__
from .qtui import UIDoc, getUiPaths
from .qtuibuild import UIBuild
from .cli_utils import (
    setup_loguru_rich_logging,
    show_processing_summary,
    show_parallel_results,
    log_performance_metrics,
)
from .merge_utils import merge_yaml_files, merge_toc_files
from .update_worker import process_ui_file_worker
import concurrent.futures as _cf
import time as _time


def clipaths(uidir, uipath):
    """Get UI paths from directory or single path."""
    if uidir:
        uipaths = getUiPaths(dir=uidir)
    elif uipath:
        uipaths = getUiPaths(path=uipath)
    else:
        uipaths = []
        sys.stderr.write("-d or -u is required\n")
        sys.exit(2)
    return uipaths


def cleanUpYamlData(data, allowEmpty=True):
    """Clean up YAML data structure."""
    for ctr in data:
        od = data[ctr]
        nd = {}
        for k, v in sorted(od.items(), key=lambda t: t[0]):
            if allowEmpty:
                nd[k] = v
            else:
                if v.lstrip().rstrip():
                    nd[k] = v
        data[ctr] = nd
    return data


class QtUIDocTools:
    """
    qtuidoctools

    Update a recursive folder with .ui Qt XML files or single .ui file
    and YAML files, or build the YAML files into a JSON

    For more help type: qtuidoctools update --help

    Update only UI: qtuidoctools update --uidir=UIDIR

    Update UI & YAML: qtuidoctools update --uidir=UIDIR --tocyaml=helptips.yaml --outyamldir=yaml

    Build JSON: qtuidoctools build --json=helptips.json --toc=helptips.yaml --dir=yaml
    """

    def version(self):
        """Show the version and exit."""
        print(f"qtuidoctools, version {__version__}")

    def update(
        self,
        uidir=None,
        uixml=None,
        tocyaml=None,
        outyamldir=None,
        nosavexml=False,
        tooltipstoxml=False,
        tooltipstoyaml=False,
        replaceinyaml=False,
        emptytoyaml=False,
        alwayssaveyaml=False,
        verbose=False,
        quiet=False,
        # parallel/merge controls
        workers: int | None = 8,
        mergeprefer: str = "nonempty",
        mergestrict: bool = False,
        dryrun: bool = False,
    ):
        """
        -d UIDIR | -u UI -t TOC -o YAMLDIR  (UI file or folder to YAML files)

        Update only UI: qtuidoctools update --uidir=UIDIR

        Update UI & YAML: qtuidoctools update --uidir=UIDIR --tocyaml=helptips.yaml --outyamldir=yaml

        Args:
            uidir: input QtUI recursive dir
            uixml: input QtUI file
            tocyaml: output TOC file
            outyamldir: folder for output YAML files
            nosavexml: do not save the the modified QtUI files
            tooltipstoxml: copy YAML h.tip to QtUI toolTip
            tooltipstoyaml: copy QtUI toolTip to YAML h.tip
            replaceinyaml: replace YAML h.nam (deletes local edits!)
            emptytoyaml: write empty properties to YAML
            alwayssaveyaml: always save YAML
            verbose: print detailed information
            quiet: only print warnings and errors
        """
        # Configure logging
        setup_loguru_rich_logging(verbose=bool(verbose), quiet=bool(quiet))
        if verbose:
            logLevel = "DEBUG"
        elif quiet:
            logLevel = "WARNING"
        else:
            logLevel = "INFO"
        uipaths = sorted(clipaths(uidir, uixml))
        total = len(uipaths)

        use_parallel = bool(workers) and workers > 1

        if not use_parallel:
            # Sequential path (legacy behavior)
            for ui_file_path in uipaths:
                uid = UIDoc(
                    uipath=ui_file_path,
                    tocpath=tocyaml,
                    yamldir=outyamldir,
                    emptytoyaml=emptytoyaml,
                    alwayssaveyaml=alwayssaveyaml,
                    logLevel=logLevel,
                )
                if tocyaml:
                    uid.updateToc()
                uid.rebuildStatusTipsInXml = True
                if tooltipstoxml:
                    uid.replaceToolTipsInXml = True
                if tooltipstoyaml:
                    uid.replaceToolTipsInYaml = True
                if outyamldir:
                    uid.updateYaml = True
                    uid.replaceNamInYaml = replaceinyaml
                uid.updateXmlAndYaml()
                if not nosavexml:
                    uid.saveXml()
                if outyamldir:
                    uid.saveYaml()
                if tocyaml:
                    uid.saveToc()
            return

        # Parallel execution path
        show_processing_summary(total, workers or 8, mode="parallel")
        start_ts = _time.time()
        results: list[dict] = []
        good: list[str] = []
        bad: list[dict] = []

        with _cf.ProcessPoolExecutor(max_workers=workers or 8) as ex:
            futs = [
                ex.submit(
                    process_ui_file_worker,
                    ui_file_path,
                    tocyaml,
                    outyamldir,
                    bool(emptytoyaml),
                    bool(alwayssaveyaml),
                    logLevel,
                    bool(nosavexml),
                    bool(tooltipstoxml),
                    bool(tooltipstoyaml),
                    bool(replaceinyaml),
                    bool(verbose),
                )
                for ui_file_path in uipaths
            ]
            for f in _cf.as_completed(futs):
                r = f.result()
                results.append(r)
                if r.get("success"):
                    good.append(r.get("file_path", ""))
                else:
                    bad.append(r)

        elapsed = _time.time() - start_ts
        show_parallel_results(good, bad, elapsed, verbose=bool(verbose))
        log_performance_metrics(
            logger=None,
            operation="update",
            files_count=total,
            elapsed_time=elapsed,
            workers=workers or 8,
            success_count=len(good),
            error_count=len(bad),
        )

        # Merge in main process
        if outyamldir and not dryrun:
            merge_yaml_files(
                outyamldir=outyamldir,
                all_results=results,
                verbose=bool(verbose),
                merge_prefer=str(mergeprefer).lower(),
                merge_strict=bool(mergestrict),
            )

        if tocyaml and not dryrun:
            toc_list = [r.get("toc_data") for r in results if r.get("toc_data")]
            if toc_list:
                merge_toc_files(tocyaml=tocyaml, toc_data_list=toc_list, verbose=bool(verbose))
        return

    def cleanup(
        self,
        uidir=None,
        uixml=None,
        tocyaml=None,
        outyamldir=None,
        emptytoyaml=False,
        compactyaml=False,
        verbose=False,
        quiet=False,
    ):
        """
        [-c] [-e] -d UIDIR | -u UI | -t TOC | -o YAMLDIR (do a purely technical refresh of a given component)

        Args:
            uidir: refresh QtUI recursive dir N/A
            uixml: refresh QtUI file N/A
            tocyaml: refresh TOC file
            outyamldir: refresh YAML files in folder
            emptytoyaml: write empty properties to YAML
            compactyaml: write compact
            verbose: print detailed information
            quiet: only print warnings and errors
        """
        setup_loguru_rich_logging(verbose=bool(verbose), quiet=bool(quiet))
        bigyaml = not compactyaml

        if uidir or uixml:
            if not quiet:
                print("-u or -d not implemented")
        elif tocyaml:
            with open(tocyaml) as f:
                data = oyaml.read_yaml(f)
            for k in data:
                d = data[k]
                data[k] = dict(sorted(d.items(), key=lambda t: t[0]))
            yaml = oyaml.yaml_dumps(
                data,
                compact=False,
                width=0,
                quote_strings=bigyaml,
                block_strings=bigyaml,
            )
            with open(tocyaml, "w") as f:
                f.write(yaml)
                if not quiet:
                    print(f"Saved: {tocyaml}")
        elif outyamldir:
            yamlpaths = getUiPaths(path="*.yaml", dir=outyamldir)
            for yamlpath in yamlpaths:
                with open(yamlpath) as f:
                    data = oyaml.read_yaml(f)
                data = cleanUpYamlData(data, allowEmpty=emptytoyaml)
                yaml = oyaml.yaml_dumps(
                    data,
                    compact=False,
                    width=0,
                    quote_strings=bigyaml,
                    block_strings=bigyaml,
                )
                with open(yamlpath, "w") as f:
                    f.write(yaml)
                    if not quiet:
                        print(f"Saved: {yamlpath}")

    def build(
        self,
        json=None,
        toc=None,
        dir=None,
        extra=False,
        verbose=False,
        quiet=False,
    ):
        """
        -j JSON -t TOC -d DIR (YAML files to JSON)

        Build JSON: qtuidoctools build --json=helptips.json --toc=helptips.yaml --dir=yaml

        Args:
            json: output JSON file
            toc: input TOC file
            dir: folder for input YAML files
            extra: append debug info to the help tips
            verbose: print detailed information
            quiet: only print warnings and errors
        """
        setup_loguru_rich_logging(verbose=bool(verbose), quiet=bool(quiet))
        if verbose:
            logLevel = "DEBUG"
        elif quiet:
            logLevel = "WARNING"
        else:
            logLevel = "INFO"

        uib = UIBuild(json, dir, extra, logLevel)
        uib.build()


def cli():
    """Main CLI entry point."""
    fire.Fire(QtUIDocTools)


if __name__ == "__main__":
    cli()
