"""エントリポイント（`python -m tiasjudge`）．

`QApplication` を生成してメイン画面（SPEC_01）を表示する．
ログの出力先設定はエントリポイント側の責務とする（error-handling.md「ログ」）．

起動オプション:

- `--check`: 画面を生成して即終了する（起動可能性の確認用．イベントループを回さない）
- `--preview-result`: 結果画面（SPEC_01「結果画面」）だけをダミー結果で開く（デバッグ用）
- `--failed`: `--preview-result` と併用し，診断失敗パターンで開く
- `--english`: `--preview-result` と併用し，英語表示で開く（結果画面は表示専用のため，
  プレビューでは画面内で言語を切り替えられない）
- `--verify-data`: 同梱学習データ（`data/data.csv`）を読み込み，ダミー L*a*b* で診断を
  1 回実行して終了する（画面を出さない．配布 exe の動作確認用．PLAN_02 ステップ 8）
"""

from __future__ import annotations

import argparse
import logging
import sys
from collections.abc import Sequence
from typing import TYPE_CHECKING

import numpy as np

if TYPE_CHECKING:  # pragma: no cover - 型チェック時のみ
    from numpy.typing import NDArray
    from PySide6.QtWidgets import QApplication

    from tiasjudge.core.svm import DiagnosisResult

logger = logging.getLogger(__name__)

#: `--check`（イベントループを回さずに終了する）オプション
CHECK_OPTION = "--check"

#: ログの書式（診断の入出力は `core` が INFO で記録する）
LOG_FORMAT = "%(asctime)s %(levelname)s %(name)s: %(message)s"

# --- `--preview-result` 用のダミーデータ（デバッグ表示専用．診断は実行しない）---

#: プレビューに使うダミー画像の一辺（結果画面は 1/2 に縮小して表示する）
PREVIEW_IMAGE_SIZE = 400
#: プレビューに使うダミー画像の色（舌色に近い RGB）
PREVIEW_IMAGE_RGB = (198, 120, 120)
#: プレビューのダミー L*a*b*（学習データの範囲内にある代表値）
PREVIEW_LAB = (58.0, 24.0, 12.0)
#: 成功パターンのダミースコア（%）
PREVIEW_PERCENTAGES: dict[str, int] = {
    "purple": 5,
    "dark_red": 10,
    "crimson": 25,
    "pink": 55,
    "pale": 5,
}
#: 成功パターンの上位 2 色（`PREVIEW_PERCENTAGES` を降順に並べた先頭 2 件）
PREVIEW_TOP_LABELS = ("pink", "crimson")
#: 失敗パターンの上位 2 色（`LEGACY_FAILURE_PERCENTAGES` を降順に並べた先頭 2 件）
PREVIEW_FAILED_TOP_LABELS = ("purple", "pink")


def _build_parser() -> argparse.ArgumentParser:
    """コマンドライン引数のパーサを作る．"""
    parser = argparse.ArgumentParser(
        prog="tiasjudge",
        description="舌診支援システム（TIAS）を起動する．",
    )
    parser.add_argument(
        CHECK_OPTION,
        action="store_true",
        help="画面を生成して即終了する（起動可能性の確認用）",
    )
    parser.add_argument(
        "--preview-result",
        action="store_true",
        help="結果画面だけをダミー結果で開く（デバッグ用．診断は実行しない）",
    )
    parser.add_argument(
        "--failed",
        action="store_true",
        help="--preview-result と併用し，診断失敗パターンで開く",
    )
    parser.add_argument(
        "--english",
        action="store_true",
        help="--preview-result と併用し，英語表示で開く",
    )
    parser.add_argument(
        "--verify-data",
        action="store_true",
        help="同梱学習データを読み込みダミー値で診断して終了する（配布 exe の確認用）",
    )
    return parser


def _preview_image() -> NDArray[np.uint8]:
    """プレビュー用の単色ダミー画像（BGR）を作る．"""
    red, green, blue = PREVIEW_IMAGE_RGB
    image = np.empty((PREVIEW_IMAGE_SIZE, PREVIEW_IMAGE_SIZE, 3), dtype=np.uint8)
    # OpenCV と同じ BGR 並びで埋める
    image[:, :] = (blue, green, red)
    return image


def _preview_result(*, failed: bool) -> DiagnosisResult:
    """プレビュー用のダミー診断結果を組み立てる（SVM 学習は行わない）．"""
    from tiasjudge.core.color import Lab
    from tiasjudge.core.csv_io import EVALUATION_COLUMN_COUNT
    from tiasjudge.core.svm import (
        LEGACY_FAILURE_PERCENTAGES,
        DiagnosisResult,
        FailureReason,
    )

    percentages = dict(LEGACY_FAILURE_PERCENTAGES) if failed else dict(PREVIEW_PERCENTAGES)
    top_labels = PREVIEW_FAILED_TOP_LABELS if failed else PREVIEW_TOP_LABELS
    return DiagnosisResult(
        lab=Lab(*PREVIEW_LAB),
        scores={key: value / 100 for key, value in percentages.items()},
        percentages=percentages,
        top_labels=top_labels,
        predictions=np.zeros(EVALUATION_COLUMN_COUNT, dtype=np.float32),
        failed=failed,
        failure_reason=FailureReason.OUT_OF_DOMAIN if failed else None,
    )


def _run_preview(app: QApplication, *, failed: bool, check: bool, english: bool = False) -> int:
    """結果画面だけをダミー結果で開く（`--preview-result`）．

    結果画面は表示専用（言語切替ボタンを持たない）ため，プレビューの言語は
    `--english` オプションで指定する．
    """
    # 関数内 import は起動時のみ GUI を読み込むため（`core` は GUI 非依存）
    from tiasjudge.gui.result_window import Language, ResultWindow, show_on_secondary_screen

    language = Language.ENGLISH if english else Language.JAPANESE
    logger.info("結果画面のプレビューを開きます（failed=%s・language=%s）", failed, language.value)
    window = ResultWindow(_preview_result(failed=failed), _preview_image(), language)
    if check:
        window.close()
        return 0

    # プレビューにはメイン画面が無いため，プライマリモニタを基準に別モニタを選ぶ
    show_on_secondary_screen(window, app.screens(), app.primaryScreen())
    return int(app.exec())


def _verify_bundled_data() -> int:
    """同梱学習データを読み込み，ダミー L*a*b* で診断を 1 回実行する（`--verify-data`）．

    PyInstaller で固めた exe が学習データを同梱できているか，OpenCV の SVM が
    動作するかを画面なしで確認するためのオプション．

    Returns:
        正常なら 0，学習データを読めなければ 1．
    """
    from tiasjudge.core.color import Lab
    from tiasjudge.core.csv_io import CsvIoError, load_training_data
    from tiasjudge.core.svm import diagnose

    # パス解決は GUI 側の同梱データ解決（frozen 対応）を再利用する
    from tiasjudge.gui.main_window import default_training_data_path

    path = default_training_data_path()
    logger.info("同梱学習データを確認します: %s", path)
    try:
        training = load_training_data(path)
    except CsvIoError as error:
        logger.error("学習データを読み込めません: %s", error)
        return 1

    result = diagnose(Lab(*PREVIEW_LAB), training)
    logger.info("動作確認の診断結果: %s（failed=%s）", result.percentages, result.failed)
    return 0


def main(argv: Sequence[str] | None = None) -> int:
    """アプリを起動する．戻り値はプロセスの終了コード．

    Args:
        argv: コマンドライン引数（プログラム名を含まない）．省略時は `sys.argv[1:]`．

    Returns:
        プロセスの終了コード（0 が正常終了）．
    """
    arguments = _build_parser().parse_args(list(sys.argv[1:] if argv is None else argv))
    logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)

    if arguments.verify_data:
        # 画面を作らずに同梱データと診断の動作だけを確認する
        return _verify_bundled_data()

    # GUI ツールキットの import は起動時のみに限定する（`core` は GUI 非依存）
    from PySide6.QtWidgets import QApplication

    app = QApplication.instance() or QApplication(sys.argv[:1])

    # アプリ全体（結果画面・ダイアログを含む）に旧実装と同じアイコンを適用する．
    # アイコンが無くても起動は続行する（`apply_app_icon` が None を返すだけ）
    from tiasjudge.gui.main_window import apply_app_icon

    apply_app_icon(app)

    if arguments.preview_result:
        return _run_preview(
            app,
            failed=arguments.failed,
            english=arguments.english,
            check=arguments.check,
        )

    from tiasjudge.gui.main_window import MainWindow

    window = MainWindow()
    if arguments.check:
        # 画面を構築できることだけを確認して終了する（ヘッドレス環境での確認用）
        window.close()
        return 0

    window.show()
    return int(app.exec())


if __name__ == "__main__":
    raise SystemExit(main())
