from __future__ import annotations

import os
import sqlite3
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

from cryptography.fernet import Fernet

from src import user_store


class UserStoreTests(unittest.TestCase):
    TEST_FERNET_KEY = Fernet.generate_key().decode()

    def test_password_hash_and_verify(self) -> None:
        encoded = user_store.hash_password("Password123!")
        self.assertTrue(user_store.verify_password("Password123!", encoded))
        self.assertFalse(user_store.verify_password("wrong-password", encoded))

    def test_user_create_auth_session_and_langfuse_settings(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            db_path = str(Path(tmp) / "app.sqlite")
            with patch.dict(
                os.environ,
                {"AGENT_APP_DB_PATH": db_path, "AGENT_DATA_ENCRYPTION_KEY": self.TEST_FERNET_KEY},
                clear=False,
            ):
                user_store._reset_fernet_cache()
                user_store.init_user_store()
                created = user_store.create_user(username="alice", password="Password123!", role="analyst")
                self.assertEqual(created["username"], "alice")

                authed = user_store.authenticate_user(username="alice", password="Password123!")
                self.assertIsNotNone(authed)

                session = user_store.issue_session(user_id=created["user_id"], expires_hours=2)
                validated = user_store.validate_session_token(session["token"])
                self.assertIsNotNone(validated)
                self.assertEqual(validated["user_id"], created["user_id"])

                upserted = user_store.upsert_user_langfuse_settings(
                    user_id=created["user_id"],
                    base_url="https://lf.example.com",
                    public_key="pk-user",
                    secret_key="sk-user",
                    environment="prod",
                )
                self.assertEqual(upserted["user_id"], created["user_id"])
                settings = user_store.get_user_langfuse_settings(created["user_id"])
                self.assertIsNotNone(settings)
                self.assertEqual(settings["base_url"], "https://lf.example.com")
                self.assertEqual(settings["public_key"], "pk-user")
                self.assertEqual(settings["secret_key"], "sk-user")

                conn = sqlite3.connect(db_path)
                row = conn.execute(
                    "SELECT public_key, secret_key FROM user_langfuse_settings WHERE user_id = ?",
                    (created["user_id"],),
                ).fetchone()
                conn.close()
                self.assertIsNotNone(row)
                self.assertTrue(str(row[0]).startswith("fernet:"))
                self.assertTrue(str(row[1]).startswith("fernet:"))

    def test_upsert_langfuse_settings_requires_fernet_key(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            db_path = str(Path(tmp) / "app.sqlite")
            with patch.dict(os.environ, {"AGENT_APP_DB_PATH": db_path, "AGENT_DATA_ENCRYPTION_KEY": ""}, clear=False):
                user_store._reset_fernet_cache()
                user_store.init_user_store()
                created = user_store.create_user(username="bob", password="Password123!", role="analyst")
                with self.assertRaises(RuntimeError):
                    _ = user_store.upsert_user_langfuse_settings(
                        user_id=created["user_id"],
                        base_url="https://lf.example.com",
                        public_key="pk-bob",
                        secret_key="sk-bob",
                        environment="prod",
                    )

    def test_migrate_plaintext_langfuse_settings(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            db_path = str(Path(tmp) / "app.sqlite")
            with patch.dict(
                os.environ,
                {"AGENT_APP_DB_PATH": db_path, "AGENT_DATA_ENCRYPTION_KEY": self.TEST_FERNET_KEY},
                clear=False,
            ):
                user_store._reset_fernet_cache()
                user_store.init_user_store()
                created = user_store.create_user(username="charlie", password="Password123!", role="analyst")
                conn = sqlite3.connect(db_path)
                conn.execute(
                    """
                    INSERT INTO user_langfuse_settings(user_id, base_url, public_key, secret_key, environment, updated_at)
                    VALUES (?, ?, ?, ?, ?, datetime('now'))
                    """,
                    (created["user_id"], "https://lf.example.com", "pk-plain", "sk-plain", "prod"),
                )
                conn.commit()
                conn.close()

                before = user_store.get_langfuse_settings_encryption_status()
                self.assertEqual(before["plaintext_rows"], 1)

                migrated = user_store.migrate_user_langfuse_settings_encryption(dry_run=False)
                self.assertEqual(migrated["affected_rows"], 1)
                after = user_store.get_langfuse_settings_encryption_status()
                self.assertTrue(after["fully_encrypted"])


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