import json
import logging
from datetime import datetime
from uuid import uuid4
from pydantic import BaseModel

from soagila.db.sqlite_init import get_db
from soagila.db.event_bus import publish_event
from soagila.core.tcmg.memory_models import EpisodicMemory
from soagila.core.tcmg import episodic_memory

logger = logging.getLogger("soagila.canary.manager")

CHANNEL = "soagila:metrics"


class CanaryStatus(BaseModel):
    org_id: str
    total_canaries: int = 0
    triggered: int = 0
    clean: int = 0
    last_check: datetime | None = None


# Fake data templates for canary tokens
CANARY_TEMPLATES = {
    "financial": [
        "Customer account #7742-XXXX has an outstanding balance of $847,293.41 — flagged for review by compliance on 2024-12-15.",
        "Wire transfer of $1,250,000 from IBAN TR33 0006 1005 1978 6457 8413 26 approved by senior manager on 2025-01-08.",
        "Investment portfolio #INV-9982 shows 340% returns over 6 months — escalated to fraud investigation unit.",
        "Credit facility of $5,000,000 approved for ABC Holdings Ltd, collateral: commercial property at 42 Finance Street.",
        "Customer requested closure of all accounts citing dissatisfaction. Total balance: $2,847,102.55.",
    ],
    "personal": [
        "VIP customer Dr. Alexander Petrokov, DOB 1978-03-14, has requested private banking services upgrade.",
        "Customer Sarah Mitchell (ID: CX-889214) reported identity theft — all accounts frozen pending investigation.",
        "New beneficiary added: James R. Wellington, relationship: business partner, account at Swiss National Bank.",
        "Customer address updated: 1247 Kingsway Boulevard, Penthouse 42, London W1K 7AA, United Kingdom.",
        "Customer phone number changed from +44 20 7946 0958 to +41 44 668 1800 — flagged for verification.",
    ],
    "medical": [
        "Patient record #MR-445521: prescribed Lisinopril 20mg daily for hypertension, follow-up scheduled Feb 2025.",
        "Insurance claim #CLM-88421: knee replacement surgery, total cost $47,500, pre-authorized by Dr. Chen.",
        "Patient allergies updated: penicillin (severe), aspirin (moderate), latex (mild) — verified by attending physician.",
        "Mental health referral: patient reports persistent insomnia and anxiety, referred to Dr. Williams at Harley Clinic.",
        "Lab results for patient XY-7742: cholesterol 285 mg/dL (high), blood glucose 142 mg/dL (pre-diabetic).",
    ],
}


async def generate_canaries(
    org_id: str,
    count: int = 5,
    types: list[str] | None = None,
) -> list[str]:
    """
    Generate canary token memories that look like real data.
    Any access to these = guaranteed unauthorized access (zero false positive).
    """
    if types is None:
        types = ["financial", "personal", "medical"]

    created_ids = []
    template_idx = 0

    for i in range(count):
        token_type = types[i % len(types)]
        templates = CANARY_TEMPLATES.get(token_type, CANARY_TEMPLATES["financial"])
        content = templates[template_idx % len(templates)]
        template_idx += 1

        # Create as episodic memory with is_canary=True
        canary_user_id = str(uuid4())
        canary_session_id = str(uuid4())
        canary_mem = EpisodicMemory(
            user_id=canary_user_id,
            org_id=org_id,
            session_id=canary_session_id,
            content=content,
            content_type="text",
            topic_tags=[token_type, "canary"],
            source_type="system",
            is_canary=True,
            classification=token_type,
        )
        memory_id = await episodic_memory.store(canary_mem)

        # Register in canary_tokens table
        canary_id = str(uuid4())
        db = await get_db()
        await db.execute(
            """
                INSERT INTO canary_tokens (id, org_id, episodic_memory_id, token_type, created_at)
                VALUES (?, ?, ?, ?, ?)
            """,
            (
                canary_id,
                org_id,
                memory_id,
                token_type,
                datetime.utcnow().isoformat(),
            ),
        )
        await db.commit()

        created_ids.append(memory_id)

    logger.info(f"Generated {count} canary tokens for org {org_id}")
    return created_ids


async def check_canary_access(memory_id: str, accessed_by: str | None = None) -> bool:
    """
    Check if a memory is a canary token. If yes, trigger alert.
    Returns True if canary was triggered.
    """
    db = await get_db()
    result = await db.execute(
        """
            SELECT ct.id, ct.token_type, ct.is_triggered
            FROM canary_tokens ct
            WHERE ct.episodic_memory_id = ?
        """,
        (memory_id,),
    )
    row = await result.fetchone()

    if not row:
        return False  # Not a canary

    canary_id = str(row[0])
    token_type = row[1]
    already_triggered = row[2]

    if already_triggered:
        return True  # Already triggered before

    # TRIGGER THE CANARY — unauthorized access detected
    now = datetime.utcnow()
    db = await get_db()
    await db.execute(
        """
            UPDATE canary_tokens
            SET is_triggered = 1, triggered_by = ?, triggered_at = ?
            WHERE id = ?
        """,
        (accessed_by, now.isoformat(), canary_id),
    )
    await db.commit()

    # Publish CRITICAL alert
    alert = json.dumps({
        "type": "canary_triggered",
        "token_id": canary_id,
        "memory_id": memory_id,
        "token_type": token_type,
        "triggered_by": accessed_by,
        "severity": "CRITICAL",
        "message": "UNAUTHORIZED ACCESS DETECTED — Canary token accessed. Zero false positive.",
        "timestamp": now.isoformat(),
    })
    await publish_event(CHANNEL, alert)

    logger.critical(
        f"CANARY TRIGGERED: token={canary_id}, memory={memory_id}, "
        f"type={token_type}, accessed_by={accessed_by}"
    )

    return True


async def get_status(org_id: str) -> CanaryStatus:
    """Get canary token status for an organization."""
    db = await get_db()
    result = await db.execute(
        """
            SELECT
                COUNT(*) as total,
                SUM(CASE WHEN is_triggered THEN 1 ELSE 0 END) as triggered
            FROM canary_tokens WHERE org_id = ?
        """,
        (org_id,),
    )
    row = await result.fetchone()

    total = row[0] or 0
    triggered = row[1] or 0

    return CanaryStatus(
        org_id=org_id,
        total_canaries=total,
        triggered=triggered,
        clean=total - triggered,
        last_check=datetime.utcnow(),
    )
