from __future__ import annotations

import sqlite3
import tempfile
import unittest
from contextlib import closing
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch

from app.api import apps as apps_api
from app.api import tasks as tasks_api
from app.storage.database import initialize_database
from app.storage.repository import Repository


SERVICE_CHECK = {
    "serviceName": "postgresql.service",
    "loadState": "loaded",
    "activeState": "active",
    "subState": "running",
    "unitFileState": "enabled",
    "active": True,
    "enabled": True,
    "healthy": True,
    "status": "active",
    "readinessStatus": "ready",
    "checkedAt": "2026-07-20T00:00:00Z",
    "errorMessage": None,
}


class ServiceCheckPersistenceTests(unittest.TestCase):
    def setUp(self) -> None:
        self.temp_dir = tempfile.TemporaryDirectory()
        root = Path(self.temp_dir.name)
        fake_settings = SimpleNamespace(
            db_path=root / "agent.db",
            cache_dir=root / "cache",
            log_dir=root / "logs",
        )
        self.settings_patch = patch("app.storage.database.settings", fake_settings)
        self.settings_patch.start()
        initialize_database()
        self.db_path = fake_settings.db_path
        self.repository = Repository()

    def tearDown(self) -> None:
        self.settings_patch.stop()
        self.temp_dir.cleanup()

    def test_task_component_api_round_trips_structured_service_checks(self) -> None:
        self.repository.create_task("task-1", "install", "postgresql", "PostgreSQL")
        self.repository.create_task_component("task-1", "postgresql", "postgresql", "apt", 10)
        self.repository.update_task_component(
            "task-1",
            "postgresql",
            service_check_status="healthy",
            service_checks=[SERVICE_CHECK],
        )

        with patch.object(tasks_api, "repository", self.repository):
            response = tasks_api.get_task_components("task-1")

        component = response["components"][0]
        self.assertEqual(component["serviceCheckStatus"], "healthy")
        self.assertEqual(component["serviceChecks"], [SERVICE_CHECK])

    def test_initialize_database_migrates_existing_component_tables(self) -> None:
        with closing(sqlite3.connect(self.db_path)) as connection:
            connection.execute("DROP TABLE task_components")
            connection.execute("DROP TABLE installed_components")
            connection.executescript(
                """
                CREATE TABLE installed_components (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    app_id TEXT NOT NULL,
                    component_id TEXT NOT NULL,
                    type TEXT NOT NULL,
                    install_order INTEGER NOT NULL,
                    status TEXT NOT NULL,
                    package_name TEXT,
                    package_version TEXT,
                    service_name TEXT,
                    docker_image TEXT,
                    docker_digest TEXT,
                    container_name TEXT,
                    compose_project_name TEXT,
                    installed_at TEXT NOT NULL,
                    updated_at TEXT NOT NULL,
                    UNIQUE(app_id, component_id)
                );
                CREATE TABLE task_components (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    task_id TEXT NOT NULL,
                    app_id TEXT NOT NULL,
                    component_id TEXT NOT NULL,
                    type TEXT NOT NULL,
                    install_order INTEGER NOT NULL,
                    status TEXT NOT NULL,
                    progress INTEGER DEFAULT 0,
                    current_step TEXT,
                    error_message TEXT,
                    started_at TEXT,
                    finished_at TEXT
                );
                """
            )
            connection.commit()

        initialize_database()

        with closing(sqlite3.connect(self.db_path)) as connection:
            installed_columns = {
                row[1] for row in connection.execute("PRAGMA table_info(installed_components)")
            }
            task_columns = {
                row[1] for row in connection.execute("PRAGMA table_info(task_components)")
            }
        self.assertTrue({"service_check_status", "service_checks_json"} <= installed_columns)
        self.assertTrue({"service_check_status", "service_checks_json"} <= task_columns)

    def test_installed_apps_include_persisted_service_health(self) -> None:
        self.repository.upsert_installed_app(
            "postgresql",
            "PostgreSQL",
            "16",
            "manifest-hash",
        )
        self.repository.upsert_installed_component(
            "postgresql",
            {
                "componentId": "postgresql",
                "type": "apt",
                "packageName": "postgresql",
                "version": "16+257build1",
                "serviceName": "postgresql.service",
                "serviceCheckStatus": "healthy",
                "serviceChecks": [SERVICE_CHECK],
            },
        )

        manager = SimpleNamespace(get_service_status=lambda _name: dict(SERVICE_CHECK))
        with (
            patch.object(apps_api, "repository", self.repository),
            patch.object(apps_api, "ServiceManager", return_value=manager),
        ):
            response = apps_api.installed_apps()

        self.assertEqual(response[0]["serviceCheckStatus"], "healthy")
        self.assertEqual(response[0]["serviceChecks"][0]["serviceName"], "postgresql.service")
        self.assertEqual(response[0]["serviceChecks"][0]["componentId"], "postgresql")

    def test_installed_apps_refresh_postgresql_readiness(self) -> None:
        stored_check = {
            **SERVICE_CHECK,
            "readinessType": "postgresql",
            "readinessStatus": "ready",
        }
        self.repository.upsert_installed_app(
            "postgresql",
            "PostgreSQL",
            "16",
            "manifest-hash",
        )
        self.repository.upsert_installed_component(
            "postgresql",
            {
                "componentId": "postgresql",
                "type": "apt",
                "packageName": "postgresql",
                "version": "16+257build1",
                "serviceName": "postgresql.service",
                "serviceCheckStatus": "healthy",
                "serviceChecks": [stored_check],
            },
        )
        service_manager = SimpleNamespace(get_service_status=lambda _name: dict(SERVICE_CHECK))
        apt_installer = SimpleNamespace(is_postgresql_ready=lambda: False)

        with (
            patch.object(apps_api, "repository", self.repository),
            patch.object(apps_api, "ServiceManager", return_value=service_manager),
            patch.object(apps_api, "AptInstaller", return_value=apt_installer),
        ):
            response = apps_api.installed_apps()

        self.assertEqual(response[0]["serviceCheckStatus"], "unhealthy")
        self.assertEqual(response[0]["serviceChecks"][0]["readinessStatus"], "failed")
        self.assertFalse(response[0]["serviceChecks"][0]["healthy"])


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