import sqlite3

import anyio
import pytest
from alembic import command
from fastapi.testclient import TestClient

from app.db import reset_db_caches
from app.config import get_settings
from app.main import create_app
from scripts.app_db_adopt import APP_BASELINE_REVISION, adopt_or_initialize_app_db
from tests.conftest import _make_app_alembic_config


async def _run_lifespan(app) -> None:
    async with app.router.lifespan_context(app):
        return None


def _prepare_app_db(tmp_path) -> str:
    app_database_path = tmp_path / "app_ready.db"
    app_database_url = f"sqlite:///{app_database_path}"
    command.upgrade(_make_app_alembic_config(app_database_url), "head")
    return app_database_url


def test_app_starts(client: TestClient) -> None:
    # With SPA enabled, GET / is served by the catch-all and returns index.html (200).
    # Without SPA (e.g. SPA_DIST_DIR points to empty dir), it returns 404.
    # Either way the app started successfully — just assert it is not a server error.
    response = client.get("/", follow_redirects=False)
    assert response.status_code in (200, 404)


def test_status_endpoint(client: TestClient) -> None:
    response = client.get("/status")
    assert response.status_code == 200
    assert response.json() == {"status": "ok"}


def test_app_start_fails_when_app_db_missing(tmp_path, monkeypatch: pytest.MonkeyPatch) -> None:
    missing_app_path = tmp_path / "missing_app.db"

    monkeypatch.setenv("APP_DATABASE_URL", f"sqlite:///{missing_app_path}")
    monkeypatch.setenv("AUTH_BOOTSTRAP_USERNAME", "admin")
    monkeypatch.setenv("AUTH_BOOTSTRAP_PASSWORD", "test-password")
    get_settings.cache_clear()
    reset_db_caches()

    app = create_app()
    with pytest.raises(RuntimeError, match="Run 'python scripts/app_db_adopt.py' first"):
        anyio.run(_run_lifespan, app)

    assert not missing_app_path.exists()

    get_settings.cache_clear()
    reset_db_caches()


def test_app_db_adoption_initializes_new_database(tmp_path) -> None:
    database_url = f"sqlite:///{tmp_path / 'app_init.db'}"

    result = adopt_or_initialize_app_db(database_url)

    assert result == "initialized"
    conn = sqlite3.connect(tmp_path / "app_init.db")
    try:
        revision = conn.execute("SELECT version_num FROM alembic_version").fetchone()[0]
        assert revision == APP_BASELINE_REVISION
        tables = {
            row[0]
            for row in conn.execute(
                "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'"
            ).fetchall()
        }
        assert {"auth_users", "auth_sessions", "app_config", "alembic_version"} <= tables
    finally:
        conn.close()


def test_app_start_seeds_missing_config_from_env_without_overwriting_existing_values(
    tmp_path, monkeypatch: pytest.MonkeyPatch
) -> None:
    app_database_url = _prepare_app_db(tmp_path)

    app_database_path = tmp_path / "app_ready.db"
    conn = sqlite3.connect(app_database_path)
    conn.execute(
        "INSERT INTO app_config (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)",
        ("APP_NAME", "Database Owned Name"),
    )
    conn.commit()
    conn.close()

    monkeypatch.setenv("APP_DATABASE_URL", app_database_url)
    monkeypatch.setenv("AUTH_BOOTSTRAP_USERNAME", "admin")
    monkeypatch.setenv("AUTH_BOOTSTRAP_PASSWORD", "test-password")
    monkeypatch.setenv("APP_NAME", "Bootstrap Name")
    monkeypatch.setenv("HOME_ASSISTANT_BASE_URL", "http://bootstrap-ha.local:8123")
    get_settings.cache_clear()
    reset_db_caches()

    app = create_app()
    anyio.run(_run_lifespan, app)

    conn = sqlite3.connect(app_database_path)
    try:
        rows = dict(conn.execute("SELECT key, value FROM app_config").fetchall())
    finally:
        conn.close()

    assert rows["APP_NAME"] == "Database Owned Name"
    assert rows["HOME_ASSISTANT_BASE_URL"] == "http://bootstrap-ha.local:8123"
    assert rows["AUTH_SESSION_COOKIE_NAME"] == "home_automation_session"

    get_settings.cache_clear()
    reset_db_caches()


def test_app_start_syncs_app_hostname_from_env_even_when_db_has_old_value(
    tmp_path, monkeypatch: pytest.MonkeyPatch
) -> None:
    app_database_url = _prepare_app_db(tmp_path)

    app_database_path = tmp_path / "app_ready.db"
    conn = sqlite3.connect(app_database_path)
    conn.execute(
        "INSERT INTO app_config (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)",
        ("APP_HOSTNAME", "old.example.com"),
    )
    conn.commit()
    conn.close()

    monkeypatch.setenv("APP_DATABASE_URL", app_database_url)
    monkeypatch.setenv("AUTH_BOOTSTRAP_USERNAME", "admin")
    monkeypatch.setenv("AUTH_BOOTSTRAP_PASSWORD", "test-password")
    monkeypatch.setenv("APP_HOSTNAME", "new.example.com")
    get_settings.cache_clear()
    reset_db_caches()

    app = create_app()
    anyio.run(_run_lifespan, app)

    conn = sqlite3.connect(app_database_path)
    try:
        rows = dict(conn.execute("SELECT key, value FROM app_config").fetchall())
    finally:
        conn.close()

    assert rows["APP_HOSTNAME"] == "new.example.com"

    get_settings.cache_clear()
    reset_db_caches()
