from typing import Dict, List, Union, Optional, Literal, Any
from pydantic import BaseModel, Field, field_validator, model_validator

class Property(BaseModel):
    property_id: str

class AutomationBaseModel(Property):
    automation_id:str

class DataResponse(BaseModel):
    status: str = 'ok'
    message: str
    data: Union[List[Dict[str, Any]], Dict[str, Any]]
# ───────────────────── Trigger Config ─────────────────────

class TriggerSpecific(BaseModel):
    field: Literal["specific"]
    spefific: List[str]   # keeping original typo for compatibility


class TriggerDays(BaseModel):
    field: Literal["days"]
    daysInterval: int
    triggerAtHour: int
    triggerAtMinute: int


TriggerConfigItem = Union[TriggerSpecific, TriggerDays]


# ───────────────────── API Call Config ─────────────────────

class ApiCallConfig(BaseModel):
    method: str
    url: str
    headers: Dict[str, str]
    body: Dict[str, Any]


# ───────────────────── Messaging Config ─────────────────────

class MessageConfig(BaseModel):
    type: Literal["message"]
    content_id: str
    channels: List[str]
    is_priority: bool


# ───────────────────── Audience Config ─────────────────────

class AudienceConfig(BaseModel):
    audience_id: str


# ───────────────────── Switch / Condition Config ─────────────────────

class ConditionRule(BaseModel):
    dimension: str
    condition: str
    value: List[str]


class SwitchPath(BaseModel):
    id: str
    name: str
    type: Literal["condition", "others"]
    target: str
    description: Optional[str] = None
    condition: Optional[List[ConditionRule]] = None


class SwitchConfig(BaseModel):
    option: Literal["all", "any"]
    conditions: List[SwitchPath]

# ───────────────────── Waiting Config ─────────────────────
class WaitConfig(BaseModel):
    duration: int
    unit: Literal["seconds", "minutes", "hours", "days"]
    description: Optional[str] = None


# ───────────────────── Node Config Union ─────────────────────

NodeConfig = Union[
    List[TriggerConfigItem],   # trigger
    ApiCallConfig,             # api_call
    MessageConfig,             # facebook / line
    AudienceConfig,            # audience
    SwitchConfig,              # switchcase
    WaitConfig                 # wait
]


# ───────────────────── Node Model ─────────────────────

class Node(BaseModel):
    node_id: str
    name: str
    type: Literal["trigger", "audience", "api_call", "facebook", "line_api", "switchcase", "wait", "sms", "email"]
    config: NodeConfig
    datebegin: Optional[str] = None  # For trigger nodes: start date/time
    dateend: Optional[str] = None    # For trigger nodes: end date/time


# ───────────────────── Settings ─────────────────────

class WorkflowSettings(BaseModel):
    timezone: int
    error_handling: Literal["continue", "stop"]


# ───────────────────── Workflow Root ─────────────────────

class WorkflowModel(BaseModel):
    automation_id: str
    property_id: str
    automation_name: str
    current_version: int
    description: str

    nodes: Dict[str, Node]
    connections: Dict[str, List[str]]
    settings: WorkflowSettings
    structure: Optional[Dict[str, Any]] = None  # Frontend flow data format (x, y coordinates, settings format)

    # Validate automation_id
    # Allow empty string for create operations (backend will generate UUID)
    @field_validator("automation_id")
    @classmethod
    def non_empty_automation_id(cls, v):
        # Allow empty string - backend will generate UUID for create operations
        if v is None:
            raise ValueError("automation_id must not be None")
        # Empty string is allowed (will be replaced by backend)
        # Return a placeholder value that will pass validation but backend will replace
        if v == "":
            return "temp-create-id"  # Temporary value, backend will replace with UUID
        return v

    # Cross-field validation: nodes <-> connections
    @model_validator(mode="after")
    def validate_graph(self):
        node_ids = set(self.nodes.keys())

        for source, targets in self.connections.items():
            if source not in node_ids:
                raise ValueError(f"Connection source '{source}' not found in nodes")

            for target in targets:
                if target not in node_ids:
                    raise ValueError(f"Connection target '{target}' not found in nodes")

        return self
    
class DraftWorkflow(BaseModel):
    property_id: str
    automation_name: str
    current_version: int

    nodes: Dict[str, Node] = Field(default_factory=dict)
    connections: Dict[str, List[str]] = Field(default_factory=dict)
    settings: WorkflowSettings

class UpdateAutomationStatusModel(Property):
    automation_id: str
    status: bool


#---Respone Mdel --
class CreateUpdateWorkflowResponse(BaseModel):
    status:str = 'ok'
    automation_id: str
    message: str