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

class DeploymentPlan(BaseModel):
    """Model for the generated deployment configuration."""
    docker_compose: str = Field(description="The full, valid docker-compose.yml content")
    intent_checklist: List[str] = Field(
        default_factory=list, 
        description="A list of high-level goals for setup (e.g. 'Migrate database', 'Install node modules')."
    )

    @model_validator(mode="before")
    @classmethod
    def migrate_run_commands(cls, data: Any) -> Any:
        if isinstance(data, dict):
            # If agent used 'run_commands' instead of 'intent_checklist'
            if "run_commands" in data and not data.get("intent_checklist"):
                old_cmds = data.pop("run_commands")
                new_list = []
                if isinstance(old_cmds, list):
                    for item in old_cmds:
                        if isinstance(item, str):
                            new_list.append(item)
                        elif isinstance(item, dict):
                            # Extract command or label
                            val = item.get("command") or item.get("label") or str(item)
                            new_list.append(val)
                data["intent_checklist"] = new_list
            
            # Ensure intent_checklist is a list
            ic = data.get("intent_checklist")
            if isinstance(ic, str):
                data["intent_checklist"] = [ic]
            elif ic is None:
                data["intent_checklist"] = []
                
        return data


class CommandDiscovery(BaseModel):
    """Model for a discovered CLI command."""
    command: str = Field(description="The full shell command discovered")
    label: str = Field(description="Friendly name for the command")
    service: str = Field(description="The service name")
    skip: bool = Field(False, description="Set to true if no suitable command found")


class AppStructure(BaseModel):
    """Model for the discovered application configuration structure."""
    env: Optional[str] = Field(None, description=".env filename or null")
    main: Optional[str] = Field(None, description="Main config file path or null")
    monitor_targets: List[str] = Field(default_factory=list, description="List of file patterns to monitor")
    monitoring_mode: str = Field("hybrid", description="Monitoring mode: file, otel, or hybrid")

    @field_validator("monitor_targets", mode="before")
    @classmethod
    def coerce_list(cls, v):
        if isinstance(v, str):
            return [v]
        if v is None:
            return []
        return v

    @field_validator("monitoring_mode", mode="before")
    @classmethod
    def normalize_mode(cls, v):
        mode = str(v or "hybrid").strip().lower()
        return mode if mode in {"file", "otel", "hybrid"} else "hybrid"

class ExecutionResult(BaseModel):
    """Result of the execution phase."""
    success: bool = Field(description="Whether the application started successfully")
    message: str = Field(description="Summary of the execution result")
    access_urls: List[str] = Field(default_factory=list, description="List of URLs to access the application")
