from __future__ import annotations

import json
from datetime import datetime
from typing import Optional, Dict, Any
from sqlmodel import SQLModel, Field
from sqlalchemy import Column, Text, UniqueConstraint


class TradeshowImport(SQLModel, table=True):
    __tablename__ = "tradeshow_imports"
    __table_args__ = (
        UniqueConstraint(
            "account_id",
            "content_hash",
            "idem_key",
            name="uq_tradeshow_import_acc_content_idem",
        ),
        {"extend_existing": True},
    )

    id: str = Field(primary_key=True)
    account_id: str = Field(index=True)
    content_hash: str = Field(index=True)
    idem_key: Optional[str] = Field(default=None, index=True)
    status: str = Field(default="accepted")
    config_version: Optional[str] = Field(default=None)
    meta_json: Optional[str] = Field(sa_column=Column(Text), default=None)
    artifact_key: Optional[str] = Field(default=None)
    sample_key: Optional[str] = Field(default=None)
    created_at: datetime = Field(default_factory=datetime.utcnow)
    updated_at: datetime = Field(default_factory=datetime.utcnow)


class TradeshowReport(SQLModel, table=True):
    __tablename__ = "tradeshow_reports"
    __table_args__ = {"extend_existing": True}

    job_id: str = Field(primary_key=True)
    account_id: str = Field(index=True)
    matched_count: int = Field(default=0)
    new_count: int = Field(default=0)
    undo_token: Optional[str] = Field(default=None)
    summary_json: Optional[str] = Field(sa_column=Column(Text), default=None)
    created_at: datetime = Field(default_factory=datetime.utcnow)


class TradeshowRepo:
    def __init__(self, db):
        self.db = db

    async def upsert_import_record(
        self,
        *,
        import_id: str,
        account_id: str,
        content_hash: str,
        idem_key: Optional[str],
        meta: Dict[str, Any],
        status: str,
        config_version: Optional[str] = None,
        artifact_key: Optional[str] = None,
        sample_key: Optional[str] = None,
    ) -> str:
        row = await self.db.get(TradeshowImport, import_id)
        if row is None:
            row = TradeshowImport(
                id=import_id,
                account_id=account_id,
                content_hash=content_hash,
                idem_key=idem_key,
                status=status,
                config_version=config_version,
                meta_json=json.dumps(meta or {}),
                artifact_key=artifact_key,
                sample_key=sample_key,
            )
            self.db.add(row)
        else:
            row.status = status
            row.config_version = config_version
            row.meta_json = json.dumps(meta or {})
            row.artifact_key = artifact_key
            row.sample_key = sample_key
            row.updated_at = datetime.utcnow()
        await self.db.commit()
        return row.id

    async def get_import(self, import_id: str) -> Optional[Dict[str, Any]]:
        row = await self.db.get(TradeshowImport, import_id)
        if not row:
            return None
        meta = {}
        try:
            meta = json.loads(row.meta_json or "{}")
        except Exception:
            meta = {}
        return {
            "id": row.id,
            "account_id": row.account_id,
            "content_hash": row.content_hash,
            "idem_key": row.idem_key,
            "status": row.status,
            "config_version": row.config_version,
            "meta": meta,
            "artifact_key": row.artifact_key,
            "sample_key": row.sample_key,
            "created_at": row.created_at,
            "updated_at": row.updated_at,
        }

    async def get_import_by_key(
        self, account_id: str, content_hash: str, idem_key: Optional[str]
    ) -> Optional[Dict[str, Any]]:
        from sqlalchemy import select

        q = (
            select(TradeshowImport)
            .where(
                TradeshowImport.account_id == str(account_id),
                TradeshowImport.content_hash == content_hash,
                TradeshowImport.idem_key == (idem_key or None),
            )
            .limit(1)
        )
        row = (await self.db.execute(q)).scalars().first()
        if not row:
            return None
        try:
            meta = json.loads(row.meta_json or "{}")
        except Exception:
            meta = {}
        return {
            "id": row.id,
            "account_id": row.account_id,
            "content_hash": row.content_hash,
            "idem_key": row.idem_key,
            "status": row.status,
            "config_version": row.config_version,
            "meta": meta,
            "artifact_key": row.artifact_key,
            "sample_key": row.sample_key,
            "created_at": row.created_at,
            "updated_at": row.updated_at,
        }

    async def save_report(
        self,
        *,
        job_id: str,
        account_id: str,
        matched_count: int,
        new_count: int,
        undo_token: str,
        summary: Dict[str, Any],
    ) -> None:
        existing = await self.db.get(TradeshowReport, job_id)
        if existing:
            existing.account_id = account_id
            existing.matched_count = matched_count
            existing.new_count = new_count
            existing.undo_token = undo_token
            existing.summary_json = json.dumps(summary or {})
            self.db.add(existing)
        else:
            row = TradeshowReport(
                job_id=job_id,
                account_id=account_id,
                matched_count=matched_count,
                new_count=new_count,
                undo_token=undo_token,
                summary_json=json.dumps(summary or {}),
            )
            self.db.add(row)
        await self.db.commit()

    async def get_report(self, job_id: str) -> Optional[Dict[str, Any]]:
        row = await self.db.get(TradeshowReport, job_id)
        if not row:
            return None
        try:
            summary = json.loads(row.summary_json or "{}")
        except Exception:
            summary = {}
        return {
            "job_id": row.job_id,
            "account_id": row.account_id,
            "matched_count": row.matched_count or 0,
            "new_count": row.new_count or 0,
            "undo_token": row.undo_token or "",
            "summary": summary or {"status": "running"},
        }
