from __future__ import annotations

import io
import json
import os
import sys
import tempfile
import unittest
from pathlib import Path
from typing import cast
from unittest.mock import patch

from flet_plus import project
from flet_plus import stdio


class DistromateConfigTests(unittest.TestCase):
    def test_build_distromate_project_config_uses_flet_defaults(self) -> None:
        config = cast(
            dict[str, object],
            project._build_distromate_project_config(
                Path("/repo"),
                ["--name", "demo", "demo.py"],
            ),
        )
        source = cast(dict[str, object], config["source"])
        package = cast(dict[str, object], config["package"])

        self.assertEqual(config["productName"], "demo")
        self.assertEqual(package["name"], "demo")
        self.assertEqual(source["type"], "native")
        self.assertEqual(source["root"], "dist")
        expected_executable = "dist/demo.exe" if os.name == "nt" else "dist/demo"
        self.assertEqual(source["executable"], expected_executable)
        self.assertEqual(source["files"], ["**/*", "!**/.git/**", "!**/*.map", "!**/*.pdb"])

    def test_build_distromate_project_config_applies_overlay_and_locks_runtime_paths(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            project_dir = Path(temp_dir)
            config = cast(
                dict[str, object],
                project._build_distromate_project_config(
                    project_dir,
                    ["--name", "RuntimeDemo", "--product-name", "Runtime Demo", "app.py"],
                    base_context={
                        "productName": "Overlay App",
                        "appId": "com.example.demo",
                        "publish": {"appId": "app_123"},
                        "package": {
                            "name": "overlay-demo",
                            "shortDescription": "Overlay description",
                            "publisher": "Overlay Inc.",
                        },
                    },
                ),
            )

        expected_executable = "dist/RuntimeDemo.exe" if os.name == "nt" else "dist/RuntimeDemo"
        publish = cast(dict[str, object], config["publish"])
        source = cast(dict[str, object], config["source"])
        package = cast(dict[str, object], config["package"])
        self.assertEqual(config["productName"], "Overlay App")
        self.assertEqual(config["appId"], "com.example.demo")
        self.assertEqual(publish["appId"], "app_123")
        self.assertEqual(package["name"], "overlay-demo")
        self.assertEqual(package["shortDescription"], "Overlay description")
        self.assertEqual(package["publisher"], "Overlay Inc.")
        self.assertEqual(source["type"], "native")
        self.assertEqual(source["root"], "dist")
        self.assertEqual(source["executable"], expected_executable)

    def test_build_distromate_project_config_strips_adapter_only_source_fields(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            project_dir = Path(temp_dir)
            config = cast(
                dict[str, object],
                project._build_distromate_project_config(
                    project_dir,
                    ["--name", "demo", "demo.py"],
                ),
            )

        source = cast(dict[str, object], config["source"])
        self.assertEqual(source["type"], "native")
        self.assertNotIn("plugin", source)
        self.assertNotIn("options", source)


class StdioTests(unittest.TestCase):
    def test_build_prepared_result_runs_flet_and_returns_generated_config(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            project_dir = Path(temp_dir)
            (project_dir / "pyproject.toml").write_text(
                "[project]\n"
                'name = "distromate-flet-sample"\n'
                'version = "1.2.3"\n',
                encoding="utf-8",
            )
            with patch("flet_plus.stdio._run_flet_pack_command", return_value=0) as run_command:
                result = stdio._build_prepared_result(
                    "prepare-package",
                    {
                        "projectDir": str(project_dir),
                        "options": {
                            "fletArgs": ["--name", "demo", "app.py"],
                            "version": "1.2.3",
                        },
                    },
                )

        run_command.assert_called_once_with(["--yes", "--name", "demo", "app.py"], project_dir)
        defaults = cast(dict[str, object], result["defaults"])
        self.assertEqual(defaults["version"], "1.2.3")
        source = cast(dict[str, object], result["source"])
        self.assertEqual(source["type"], "native")
        self.assertNotIn("project", result)

    def test_stdio_main_returns_manifest(self) -> None:
        stdin = io.StringIO(json.dumps({"protocol": "distromate.plugin", "version": 3, "command": "manifest"}))
        stdout = io.StringIO()
        with patch("sys.stdin", stdin), patch("sys.stdout", stdout):
            rc = stdio.main()

        self.assertEqual(rc, 0)
        response = json.loads(stdout.getvalue())
        self.assertEqual(response["ok"], True)
        self.assertEqual(response["protocol"], "distromate.plugin")
        self.assertEqual(response["version"], 3)
        self.assertEqual(response["manifest"]["kind"], "builder")
        self.assertEqual(response["manifest"]["name"], "flet")


if __name__ == "__main__":
    unittest.main()
