import uuid
from datetime import date, datetime
from typing import Any, Literal

from fastapi import HTTPException
from pydantic import BaseModel, Field, model_validator

from api.classification import validate_classification
from hub_core.schemas.managed_repo import (
    RepoCreate as CoreRepoCreate,
    RepoPathRegister,
    RepoRead as CoreRepoRead,
)


class ClassificationFields(BaseModel):
    category: str | None = None
    secondary_domains: list[str] | None = None
    capability_tags: list[str] | None = None
    business_stake: list[str] | None = None
    business_mechanics: list[str] | None = None
    classified_at: date | None = None
    classified_by: str | None = None
    standard_version: str | None = None


def classification_fields_set(data: dict[str, Any]) -> bool:
    keys = (
        "category",
        "secondary_domains",
        "capability_tags",
        "business_stake",
        "business_mechanics",
        "classified_at",
        "classified_by",
        "standard_version",
    )
    return any(data.get(key) is not None for key in keys)


def validate_repo_classification_fields(
    *,
    domain_slug: str,
    fields: dict[str, Any],
    require_complete: bool = False,
) -> dict[str, Any]:
    """Validate classification fields and return normalized values for persistence."""
    if not classification_fields_set(fields) and not require_complete:
        return fields

    block = {
        "category": fields.get("category"),
        "domain": domain_slug,
        "secondary_domains": fields.get("secondary_domains") or [],
        "capability_tags": fields.get("capability_tags") or [],
        "business_stake": fields.get("business_stake") or [],
        "business_mechanics": fields.get("business_mechanics") or [],
    }
    if require_complete or fields.get("category") is not None:
        if block["category"] is None:
            raise HTTPException(status_code=422, detail="`category` is required when classification is provided")
    if classification_fields_set(fields) and block["category"] is not None:
        errors, warnings = validate_classification(block)
        if errors:
            raise HTTPException(status_code=422, detail={"classification_errors": errors, "warnings": warnings})
    return fields


class RepoCreate(CoreRepoCreate, ClassificationFields):
    topic_id: uuid.UUID | None = None

    @model_validator(mode="after")
    def validate_classification_on_create(self) -> "RepoCreate":
        validate_repo_classification_fields(
            domain_slug=self.domain_slug,
            fields=self.model_dump(),
            require_complete=classification_fields_set(self.model_dump()),
        )
        return self


class RepoUpdate(ClassificationFields):
    name: str | None = None
    local_path: str | None = None
    remote_url: str | None = None
    git_fingerprint: str | None = None
    description: str | None = None
    topic_id: uuid.UUID | None = None
    last_state_synced_at: datetime | None = None


class RepoOnboardRequest(BaseModel):
    """Start scripted onboarding for a working copy that is visible to State Hub."""
    domain_slug: str
    project_path: str
    agent_profile: Literal["claude-code", "codex"] = "codex"
    additional: bool = False


class RepoOnboardResult(BaseModel):
    ok: bool
    repo_slug: str | None = None
    agent_profile: str
    command: list[str]
    stdout: str = ""
    stderr: str = ""


class RepoRead(CoreRepoRead, ClassificationFields):
    topic_id: uuid.UUID | None = None
    sbom_source: str | None = None
    last_sbom_at: datetime | None = None
    last_state_synced_at: datetime | None = None
    created_at: datetime
    updated_at: datetime


class DispatchTask(BaseModel):
    id: uuid.UUID
    title: str
    priority: str
    status: str
    needs_human: bool


class DispatchWorkplan(BaseModel):
    id: uuid.UUID
    title: str
    status: str
    pending_tasks: list[DispatchTask]


# Legacy alias
DispatchWorkstream = DispatchWorkplan


class PendingInterfaceChange(BaseModel):
    id: uuid.UUID
    title: str
    change_type: str
    interface_type: str
    origin_repo_slug: str
    affected_paths: list[str]
    planned_for: date | None
    published_at: datetime | None


class ScopeIssueDetail(BaseModel):
    id: str
    label: str
    status: str
    detail: str
    missing_sections: list[str] = Field(default_factory=list)
    invalid_capability_blocks: list[dict[str, Any]] = Field(default_factory=list)
    needs_refresh_sections: list[str] = Field(default_factory=list)


class RepoDispatch(BaseModel):
    repo_slug: str
    active_goal: dict[str, Any] | None
    active_workplans: list[DispatchWorkplan]
    human_interventions: list[DispatchTask]
    pending_interface_changes: list[PendingInterfaceChange]
    scope_needs_review: bool
    scope_issue_details: list[ScopeIssueDetail]
    last_state_synced_at: datetime | None

    @property
    def active_workstreams(self) -> list[DispatchWorkplan]:
        return self.active_workplans


class RepoScopeHealth(BaseModel):
    repo_slug: str
    domain_slug: str | None = None
    local_path: str | None = None
    path_available: bool
    scope_needs_review: bool
    scope_issue_details: list[ScopeIssueDetail]