from typing import Optional, Dict, Any, List
from sqlalchemy import Index, Column, JSON as SQLJSON, DateTime, Text, Integer, String
from sqlmodel import Field
from datetime import datetime
from fmatch.saas.db import Base
import uuid


class L2APolicy(Base, table=True):
    __tablename__ = "l2a_policies"

    id: str = Field(primary_key=True, default_factory=lambda: str(uuid.uuid4()))
    account_id: str = Field(foreign_key="accounts.id")
    integration_id: str = Field(foreign_key="integrations.id")

    # which Lead lookup we will write (if present)
    lead_lookup_field: Optional[str] = Field(
        default=None
    )  # e.g. "Account__c" - DEPRECATED

    # Writeback field configuration
    link_field_api_name: Optional[str] = Field(
        default=None, max_length=120
    )  # Salesforce field API name
    link_overwrite_mode: str = Field(
        default="if_empty", max_length=20
    )  # 'if_empty' | 'always' | 'never'

    # Ensemble scoring configuration
    use_multi_algo: bool = Field(
        default=False
    )  # Enable multi-algorithm ensemble scoring

    # Upgrade configuration
    upgrade_min_gain: Optional[float] = Field(default=0.10)
    upgrade_min_score: Optional[float] = Field(default=0.80)
    upgrade_anchor_required: Optional[bool] = Field(default=False)
    upgrade_enablement: Optional[str] = Field(
        default="disabled"
    )  # "disabled" | "pilot" | "full"

    # strategy thresholds & weights
    thresholds: Dict[str, float] = Field(
        sa_column=Column(SQLJSON),
        default_factory=lambda: {"suggest": 0.75, "auto_link": 0.95},
    )
    weights: Dict[str, float] = Field(
        sa_column=Column(SQLJSON),
        default_factory=lambda: {
            "domain": 0.45,
            "name": 0.30,
            "phone": 0.15,
            "geo": 0.10,
        },
    )

    # governance
    auto_link_tier: str = Field(default="DISABLED")  # "DISABLED"|"CERTAIN"
    status: str = Field(default="ACTIVE")
    created_at: datetime = Field(default_factory=datetime.utcnow)
    updated_at: datetime = Field(
        default_factory=datetime.utcnow,
        sa_column=Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow),
    )
    last_run_at: Optional[datetime] = Field(default=None)  # Track when L2A was last run

    __table_args__ = (
        Index("idx_l2a_policy_account", "account_id", "status"),
        {"extend_existing": True},
    )


class L2ASuggestion(Base, table=True):
    __tablename__ = "l2a_suggestions"
    __table_args__ = {"extend_existing": True}

    id: str = Field(primary_key=True, default_factory=lambda: str(uuid.uuid4()))
    policy_id: str = Field(foreign_key="l2a_policies.id")
    lead_id: str = Field()
    account_id: str = Field()
    score: Optional[float] = Field(default=None)  # 0..1
    tier: Optional[str] = Field(default=None)  # CERTAIN/LIKELY/POSSIBLE/REVIEW
    reasons: Optional[Dict[str, Any]] = Field(
        sa_column=Column(SQLJSON), default=None
    )  # {"domain":0.42,"name":0.31,...}
    field_score_details: Optional[list] = Field(
        sa_column=Column(SQLJSON), default=None
    )  # Detailed per-field scores from engine
    explanation: Optional[str] = Field(default=None)

    # lifecycle
    status: str = Field(default="pending")  # pending, linked, rejected, error
    error_message: Optional[str] = Field(default=None)
    created_at: datetime = Field(default_factory=datetime.utcnow)
    decided_at: Optional[datetime] = Field(default=None)
    decided_by: Optional[str] = Field(default=None)

    # for undo
    applied_field: Optional[str] = Field(default=None)  # which lead lookup we set
    previous_value: Optional[str] = Field(default=None)  # old value (usually null)
    applied_value: Optional[str] = Field(default=None)  # account id we wrote
    # Bulk job linkage (for bulk undo)
    job_id: Optional[str] = Field(default=None)

    # Upgrade tracking fields
    incumbent_account_id: Optional[str] = Field(default=None)
    incumbent_score: Optional[float] = Field(default=None)

    # Registry version tracking
    registry_version: Optional[str] = Field(
        default=None, max_length=20
    )  # 12-char hash of domain registry used
    gain: Optional[float] = Field(default=None)  # best_score - incumbent_score
    reason_code: Optional[str] = Field(default="assign")  # "assign" | "upgrade"
    last_verified_at: Optional[datetime] = Field(default=None)
    incumbent_field_score_details: Optional[List] = Field(
        sa_column=Column(SQLJSON), default=None
    )  # POLISH: Evidence for incumbent

    __table_args__ = (
        Index("idx_l2a_sug_policy_status", "policy_id", "status"),
        Index("idx_l2a_sug_score", "score"),
        Index("idx_l2a_sug_lead", "lead_id"),
        Index("idx_l2a_sug_reason_gain", "reason_code", "gain"),  # NEW
        {"extend_existing": True},
    )


# Saved Views for Manual L2A Studio
class L2AManualView(Base, table=True):
    __tablename__ = "l2a_manual_views"

    id: str = Field(primary_key=True, default_factory=lambda: str(uuid.uuid4()))
    account_id: str = Field(index=True)
    user_id: str = Field(index=True)
    object_type: str = Field()  # 'Lead' | 'Account'
    name: str = Field()
    # Visibility scope for the saved view: 'user' (default) or 'org'
    scope: str = Field(default="user")
    filters_json: str = Field(sa_column=Column(Text), default="[]")
    created_at: datetime = Field(default_factory=datetime.utcnow)
    updated_at: datetime = Field(
        default_factory=datetime.utcnow,
        sa_column=Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow),
    )

    __table_args__ = (
        Index(
            "idx_l2a_manual_views_acct_user_obj", "account_id", "user_id", "object_type"
        ),
        {"extend_existing": True},
    )


class L2ABulkJob(Base, table=True):
    __tablename__ = "l2a_bulk_jobs"

    id: str = Field(primary_key=True, default_factory=lambda: str(uuid.uuid4()))
    account_id: str = Field(index=True)
    user_id: str = Field(index=True)

    kind: str = Field()  # 'manual_link'
    target_account_id: str = Field()

    # JSON-encoded arrays for portability
    filters_json: str = Field(sa_column=Column(Text), default="[]")
    scope: str = Field(default="user")  # 'user' or 'org'
    exclusions_json: str = Field(sa_column=Column(Text), default="[]")

    writeback_field: Optional[str] = Field(default=None)
    overwrite_mode: Optional[str] = Field(default=None)  # 'if_empty'|'always'|'never'

    total: int = Field(sa_column=Column(Integer), default=0)
    processed: int = Field(sa_column=Column(Integer), default=0)
    applied: int = Field(sa_column=Column(Integer), default=0)
    skipped: int = Field(sa_column=Column(Integer), default=0)
    error_count: int = Field(sa_column=Column(Integer), default=0)

    error_report: Optional[str] = Field(sa_column=Column(Text), default=None)
    status: str = Field(default="queued")  # queued|running|completed|failed|canceled

    # Approvals & actors
    approval_status: str = Field(
        default="auto", sa_column=Column(String, default="auto", nullable=False)
    )  # auto|waiting_approval|approved|rejected
    requested_by: Optional[str] = Field(default=None)
    approved_by: Optional[str] = Field(default=None)
    approved_via: Optional[str] = Field(default=None)

    # Timing / resume / ETA
    started_at: Optional[datetime] = Field(default=None)
    ended_at: Optional[datetime] = Field(default=None)
    last_cursor: Optional[str] = Field(sa_column=Column(Text), default=None)
    last_page_size: int = Field(sa_column=Column(Integer), default=0)
    eta_sec: int = Field(sa_column=Column(Integer), default=0)

    # Policy block statistics (for dry-run and bookkeeping)
    blocked_if_empty: int = Field(sa_column=Column(Integer), default=0)
    blocked_never: int = Field(sa_column=Column(Integer), default=0)

    created_at: datetime = Field(default_factory=datetime.utcnow)
    updated_at: datetime = Field(
        default_factory=datetime.utcnow,
        sa_column=Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow),
    )

    __table_args__ = (
        Index("idx_l2a_bulk_jobs_acct_user", "account_id", "user_id"),
        Index("idx_l2a_jobs_account_status", "account_id", "status"),
        {"extend_existing": True},
    )
