from datetime import datetime, timezone
from typing import Any, Literal

from pydantic import AnyUrl, Field, PastDatetime, SecretStr, field_serializer, field_validator, model_validator

from models.base import TaranisBaseModel
from models.types import (
    BOT_TYPES,
    COLLECTOR_TYPES,
    CONNECTOR_TYPES,
    PRESENTER_TYPES,
    PUBLISHER_TYPES,
    WORKER_CATEGORY,
    WORKER_TYPES,
    AttributeType,
    ItemType,
    TLPLevel,
)


def _utcnow() -> datetime:
    return datetime.now(timezone.utc).replace(tzinfo=None)


def _normalize_datetime(value: datetime | None) -> datetime | None:
    if value is None or value.tzinfo is None or value.utcoffset() is None:
        return value
    return value.astimezone(timezone.utc).replace(tzinfo=None)


class ExportStoriesQuery(TaranisBaseModel):
    timefrom: PastDatetime | None = None
    timeto: PastDatetime | None = None
    metadata: bool = False

    @field_validator("timefrom", "timeto", mode="before")
    @classmethod
    def empty_string_to_none(cls, value):
        if value == "":
            return None
        return value

    @field_validator("timefrom", "timeto", mode="after")
    @classmethod
    def normalize_utc_naive(cls, value: PastDatetime | None) -> datetime | None:
        return _normalize_datetime(value)

    @model_validator(mode="after")
    def set_timeto_default(self):
        if self.timefrom and (self.timeto is None or self.timeto < self.timefrom):
            self.timeto = _utcnow()
        return self


class Job(TaranisBaseModel):
    _core_endpoint = "/config/schedule"
    _model_name = "job"
    _pretty_name = "Scheduler"

    id: str | None = None
    name: str
    trigger: str | None = None
    kwargs: str | None = None
    next_run_time: str | None = None


class TaskResult(TaranisBaseModel):
    _core_endpoint = "/config/task-results"
    _model_name = "task_result"
    _pretty_name = "Task Result"

    id: str | None = None
    result: Any | None = None
    status: str | None = None
    last_change: datetime | None = None
    last_success: datetime | None = None

    def __eq__(self, other: object) -> bool:
        return self.status == (other.status if isinstance(other, TaskResult) else other)

    def __lt__(self, other: object) -> bool:
        other_status = other.status if isinstance(other, TaskResult) else other

        if self.status is None:
            return other_status is not None
        return False if other_status is None else str(self.status) < str(other_status)


class Address(TaranisBaseModel):
    city: str = ""
    country: str = ""
    street: str = ""
    zip: str = ""


class Organization(TaranisBaseModel):
    _core_endpoint = "/config/organizations"
    _model_name = "organization"

    id: int | None = None
    name: str = ""
    description: str | None = ""
    address: Address = Field(default_factory=Address)


class Permission(TaranisBaseModel):
    _core_endpoint = "/config/permissions"
    _model_name = "permission"
    id: str | None = None
    name: str
    description: str = ""


class ACL(TaranisBaseModel):
    _core_endpoint = "/config/acls"
    _model_name = "acl"
    _pretty_name = "ACL"

    id: int | None = None
    name: str = ""
    description: str | None = ""
    item_type: ItemType | None = None
    item_id: str | None = None

    roles: list[int] = Field(default_factory=list)

    read_only: bool = True
    enabled: bool = True


class ParameterValue(TaranisBaseModel):
    _core_endpoint = "/config/parameter-values"
    _model_name = "parameter_value"
    id: int | None = None
    parameter: str = ""
    value: str | None = ""


class Worker(TaranisBaseModel):
    _core_endpoint = "/config/worker-types"
    _model_name = "worker_type"
    _pretty_name = "Worker Type"

    id: str | None = None
    name: str
    description: str | None = ""
    type: WORKER_TYPES
    category: WORKER_CATEGORY
    parameters: dict[str, str] = Field(default_factory=dict)


class Role(TaranisBaseModel):
    _core_endpoint = "/config/roles"
    _model_name = "role"
    _pretty_name = "Role"

    id: int | None = None
    name: str = ""
    description: str | None = ""
    permissions: list[str] = Field(default_factory=list)
    tlp_level: TLPLevel | None = None


class User(TaranisBaseModel):
    _core_endpoint = "/config/users"
    _model_name = "user"

    id: int | None = None
    name: str
    organization: dict | int
    permissions: list[str] | None = None
    profile: dict | None = None
    roles: list[int] | list[dict] = Field(default_factory=list)
    username: str = ""
    password: SecretStr | None = None

    @field_serializer("password", when_used="json")
    def dump_secret(self, v):
        if isinstance(v, SecretStr):
            return v.get_secret_value()
        return v


class TaranisConfig(TaranisBaseModel):
    default_collector_proxy: AnyUrl | Literal[""] = ""
    default_collector_interval: str = ""
    default_tlp_level: TLPLevel = TLPLevel.CLEAR
    default_story_conflict_retention: str = "200"
    default_news_item_conflict_retention: str = "200"


class Settings(TaranisBaseModel):
    _core_endpoint = "/admin/settings"
    _model_name = "settings"
    _pretty_name = "Settings"
    _cache_timeout = 30
    id: int = Field(default=1, frozen=True, exclude=True)
    settings: TaranisConfig = Field(default_factory=TaranisConfig)


class WordListEntry(TaranisBaseModel):
    value: str
    category: str | None = None
    description: str | None = None


class WordList(TaranisBaseModel):
    _core_endpoint = "/config/word-lists"
    _model_name = "word_list"
    _pretty_name = "Word List"

    id: int | None = None
    name: str
    description: str | None = ""
    usage: list[str] = Field(default_factory=list)
    link: str = ""
    entries: list[WordListEntry] | None = Field(default_factory=list)


class OSINTSource(TaranisBaseModel):
    _core_endpoint = "/config/osint-sources"
    _model_name = "osint_source"
    _pretty_name = "OSINT Source"

    id: str | None = None
    name: str
    description: str = ""
    rank: int = Field(default=0, ge=0, le=5)
    type: COLLECTOR_TYPES | None = None
    parameters: dict[str, str] | None = Field(default_factory=dict)

    icon: str | None = None
    enabled: bool | None = True
    status: TaskResult | None = None


class OSINTSourceUpdateModel(TaranisBaseModel):
    name: str | None = None
    description: str | None = None
    rank: int | None = Field(default=None, ge=0, le=5)
    parameters: dict[str, str] | None = None
    icon: str | None = None


class OSINTSourceGroup(TaranisBaseModel):
    _core_endpoint = "/config/osint-source-groups"
    _model_name = "osint_source_group"
    _pretty_name = "OSINT Source Group"

    id: str | None = None
    name: str
    description: str = ""
    default: bool = False

    osint_sources: list[str] = Field(default_factory=list)
    word_lists: list[int] = Field(default_factory=list)


class ProductParameterValue(TaranisBaseModel):
    TEMPLATE_PATH: str | None = None


class ProductType(TaranisBaseModel):
    _core_endpoint = "/config/product-types"
    _model_name = "product_type"
    _pretty_name = "Product Type"

    id: int | None = None
    title: str
    description: str = ""
    type: PRESENTER_TYPES
    parameters: ProductParameterValue = Field(default_factory=ProductParameterValue)
    report_types: list[int] = Field(default_factory=list)
    status: TaskResult | None = None


class PublisherPreset(TaranisBaseModel):
    _core_endpoint = "/config/publisher-presets"
    _model_name = "publisher_preset"
    _pretty_name = "Publisher Preset"

    id: str | None = None
    name: str
    type: PUBLISHER_TYPES
    description: str | None = ""
    parameters: dict[str, str] | None = Field(default_factory=dict)
    status: TaskResult | None = None


class ReportItemAttribute(TaranisBaseModel):
    id: int | None = None
    attribute: dict[str, str] | None = None
    attribute_id: int | None = None
    attribute_group_id: int | None = None
    title: str | None = None
    description: str | None = None
    index: int | None = None
    required: bool | None = None
    value: str | None = None
    type: str | None = None


class ReportItemAttributeGroup(TaranisBaseModel):
    id: int | None = None
    title: str | None = None
    description: str | None = None
    index: int | None = None
    attribute_group_items: list[ReportItemAttribute] = Field(default_factory=list)


class ReportItemType(TaranisBaseModel):
    _core_endpoint = "/config/report-item-types"
    _model_name = "report_item_type"
    _pretty_name = "Report Item Type"

    id: int | None = None
    title: str
    description: str = ""
    attribute_groups: list[ReportItemAttributeGroup] | None = Field(default_factory=list)


class Template(TaranisBaseModel):
    _core_endpoint = "/config/templates"
    _model_name = "template"
    _pretty_name = "Template"

    id: str
    content: str | None = None
    validation_status: dict | None = None


class AttributeEnum(TaranisBaseModel):
    id: int | None = None
    index: int | None = None
    value: str | None = None
    description: str | None = None
    imported: bool | None = None
    attribute_id: int | None = None


class Attribute(TaranisBaseModel):
    _core_endpoint = "/config/attributes"
    _model_name = "attribute"
    _pretty_name = "Attribute"

    id: int | None = None
    name: str
    description: str = ""
    type: AttributeType
    default_value: str = ""
    attribute_enums: list[AttributeEnum] = Field(default_factory=list)


class Bot(TaranisBaseModel):
    _core_endpoint = "/config/bots"
    _model_name = "bot"
    _pretty_name = "Bot"

    id: str | None = None
    name: str
    description: str = ""
    type: BOT_TYPES
    index: int | None = None
    enabled: bool = True
    parameters: dict[str, str] | None = Field(default_factory=dict)
    status: TaskResult | None = None


class Connector(TaranisBaseModel):
    _core_endpoint = "/config/connectors"
    _model_name = "connector"
    _pretty_name = "Connector"

    id: str | None = None
    name: str
    description: str = ""
    type: CONNECTOR_TYPES | None = Field(default=None)
    index: int | None = None
    parameters: dict[str, str] | None = Field(default_factory=dict)
    icon: str | None = None
    status: TaskResult | None = None


class WorkerParameterValue(TaranisBaseModel):
    name: str
    label: str | None = None
    parent: Literal["parameters"] = "parameters"
    type: str | None = None
    rules: list[str] = Field(default_factory=list)


class WorkerParameter(TaranisBaseModel):
    _core_endpoint = "/config/worker-parameters"
    _model_name = "worker_parameter"
    _pretty_name = "Worker Parameter"

    id: str
    parameters: list[WorkerParameterValue]
