from __future__ import annotations

import json
import uuid
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

from mcp_eregistrations_keycloak.db.connection import get_connection
from mcp_eregistrations_keycloak.db.migrations import apply_migrations


class KcAuditLogger:
    """Lightweight SQLite audit logger for Keycloak operations."""

    def __init__(self, db_path: Path):
        self._db_path = db_path
        self._initialized = False

    @classmethod
    def for_instance(cls, instance: str | None = None) -> KcAuditLogger:
        """Create logger from instance config (mirrors KeycloakAdminClient)."""
        from mcp_eregistrations_common.config import get_instance_config
        from mcp_eregistrations_keycloak.db.connection import get_db_path

        config = get_instance_config(instance)
        keycloak_url = config.get("keycloak_url", "unknown")
        return cls(get_db_path(keycloak_url))

    async def _ensure_db(self) -> None:
        if not self._initialized:
            async with get_connection(self._db_path) as db:
                await apply_migrations(db)
            self._initialized = True

    async def record_pending(
        self,
        user_email: str | None,
        operation_type: str,
        object_type: str,
        params: dict[str, Any],
        object_id: str | None = None,
    ) -> str:
        """Record a pending operation. Returns audit_id."""
        await self._ensure_db()
        audit_id = str(uuid.uuid4())
        now = datetime.now(UTC).isoformat()
        async with get_connection(self._db_path) as db:
            await db.execute(
                """INSERT INTO audit_entries
                   (id, timestamp, user_email, operation_type, object_type,
                    object_id, params, status)
                   VALUES (?, ?, ?, ?, ?, ?, ?, 'pending')""",
                (
                    audit_id,
                    now,
                    user_email,
                    operation_type,
                    object_type,
                    object_id,
                    json.dumps(params),
                ),
            )
            await db.commit()
        return audit_id

    async def mark_success(self, audit_id: str, result: dict[str, Any]) -> None:
        """Mark an audit entry as successful."""
        async with get_connection(self._db_path) as db:
            await db.execute(
                "UPDATE audit_entries SET status='success',"
                " result=? WHERE id=? AND status='pending'",
                (json.dumps(result), audit_id),
            )
            await db.commit()

    async def mark_failed(self, audit_id: str, error_message: str) -> None:
        """Mark an audit entry as failed."""
        async with get_connection(self._db_path) as db:
            await db.execute(
                "UPDATE audit_entries SET status='failed',"
                " error_message=? WHERE id=? AND status='pending'",
                (error_message, audit_id),
            )
            await db.commit()

    async def save_rollback_state(
        self,
        audit_id: str,
        object_type: str,
        object_id: str | None,
        previous_state: dict[str, Any],
    ) -> str:
        """Save previous state for potential rollback. Returns rollback state id."""
        state_id = str(uuid.uuid4())
        now = datetime.now(UTC).isoformat()
        async with get_connection(self._db_path) as db:
            await db.execute(
                """INSERT INTO rollback_states
                   (id, audit_id, object_type, object_id, previous_state, created_at)
                   VALUES (?, ?, ?, ?, ?, ?)""",
                (
                    state_id,
                    audit_id,
                    object_type,
                    object_id,
                    json.dumps(previous_state),
                    now,
                ),
            )
            await db.commit()
        return state_id

    async def list_entries(
        self,
        limit: int = 20,
        offset: int = 0,
        object_type: str | None = None,
    ) -> list[dict[str, Any]]:
        """List audit entries with optional filtering."""
        await self._ensure_db()
        async with get_connection(self._db_path) as db:
            if object_type:
                cursor = await db.execute(
                    """SELECT * FROM audit_entries
                       WHERE object_type=?
                       ORDER BY timestamp DESC LIMIT ? OFFSET ?""",
                    (object_type, limit, offset),
                )
            else:
                cursor = await db.execute(
                    "SELECT * FROM audit_entries"
                    " ORDER BY timestamp DESC LIMIT ? OFFSET ?",
                    (limit, offset),
                )
            rows = await cursor.fetchall()
            return [
                {
                    "id": row["id"],
                    "timestamp": row["timestamp"],
                    "user_email": row["user_email"],
                    "operation_type": row["operation_type"],
                    "object_type": row["object_type"],
                    "object_id": row["object_id"],
                    "status": row["status"],
                    "params": json.loads(row["params"]) if row["params"] else None,
                    "result": json.loads(row["result"]) if row["result"] else None,
                    "error_message": row["error_message"],
                    "rolled_back_at": row["rolled_back_at"],
                }
                for row in rows
            ]

    async def get_rollback_state(self, audit_id: str) -> dict[str, Any] | None:
        """Get rollback state for an audit entry."""
        async with get_connection(self._db_path) as db:
            cursor = await db.execute(
                "SELECT * FROM rollback_states WHERE audit_id=?",
                (audit_id,),
            )
            row = await cursor.fetchone()
            if not row:
                return None
            return {
                "id": row["id"],
                "audit_id": row["audit_id"],
                "object_type": row["object_type"],
                "object_id": row["object_id"],
                "previous_state": json.loads(row["previous_state"]),
                "created_at": row["created_at"],
            }

    async def mark_rolled_back(self, audit_id: str) -> None:
        """Mark an audit entry as rolled back."""
        now = datetime.now(UTC).isoformat()
        async with get_connection(self._db_path) as db:
            await db.execute(
                "UPDATE audit_entries SET rolled_back_at=? WHERE id=?",
                (now, audit_id),
            )
            await db.commit()

    async def cleanup_old_entries(self, days: int = 30) -> int:
        """Delete rollback states older than specified days. Returns count deleted."""
        await self._ensure_db()
        async with get_connection(self._db_path) as db:
            cursor = await db.execute(
                """DELETE FROM rollback_states
                   WHERE created_at < datetime('now', ?)""",
                (f"-{days} days",),
            )
            count: int = cursor.rowcount
            await db.commit()
            return count
