"""Nextcloud deployment playbook.

Defines the steps required to:
1. Set Nextcloud domain on a tenant server (v2: via NEXTCLOUD_SET_DOMAIN task)
2. Perform initial setup via Playwright automation (create admin account)

Tenant servers must have stacks and env templates under /opt/letsbe.
"""

import uuid
from typing import Any
from urllib.parse import urlparse

from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.task import Task, TaskStatus


class CompositeStep(BaseModel):
    """A single step in a composite playbook."""

    type: str = Field(..., description="Task type (e.g., ENV_UPDATE, DOCKER_RELOAD)")
    payload: dict[str, Any] = Field(
        default_factory=dict, description="Payload for this step"
    )


# LetsBe standard paths
NEXTCLOUD_STACK_DIR = "/opt/letsbe/stacks/nextcloud"


# =============================================================================
# Initial Setup via Playwright
# =============================================================================


def build_nextcloud_initial_setup_step(
    *,
    base_url: str,
    admin_username: str,
    admin_password: str,
) -> dict[str, Any]:
    """
    Build a PLAYWRIGHT task payload for Nextcloud initial setup.

    This creates the admin account on a fresh Nextcloud installation.

    Args:
        base_url: The base URL for Nextcloud (e.g., "https://cloud.example.com")
        admin_username: Username for the admin account
        admin_password: Password for the admin account

    Returns:
        Task payload dict with type="PLAYWRIGHT"
    """
    # Extract domain from URL for allowlist
    parsed = urlparse(base_url)
    allowed_domain = parsed.netloc  # e.g., "cloud.example.com"

    return {
        "scenario": "nextcloud_initial_setup",
        "inputs": {
            "base_url": base_url,
            "admin_username": admin_username,
            "admin_password": admin_password,
        },
        "options": {
            "allowed_domains": [allowed_domain],
        },
        "timeout": 120,
    }


async def create_nextcloud_initial_setup_task(
    *,
    db: AsyncSession,
    tenant_id: uuid.UUID,
    agent_id: uuid.UUID,
    base_url: str,
    admin_username: str,
    admin_password: str,
) -> Task:
    """
    Create and persist a PLAYWRIGHT task for Nextcloud initial setup.

    Args:
        db: Async database session
        tenant_id: UUID of the tenant
        agent_id: UUID of the agent to assign the task to
        base_url: The base URL for Nextcloud
        admin_username: Username for the admin account
        admin_password: Password for the admin account

    Returns:
        The created Task object with type="PLAYWRIGHT"
    """
    payload = build_nextcloud_initial_setup_step(
        base_url=base_url,
        admin_username=admin_username,
        admin_password=admin_password,
    )

    task = Task(
        tenant_id=tenant_id,
        agent_id=agent_id,
        type="PLAYWRIGHT",
        payload=payload,
        status=TaskStatus.PENDING.value,
    )

    db.add(task)
    await db.commit()
    await db.refresh(task)

    return task


# =============================================================================
# Set Domain via NEXTCLOUD_SET_DOMAIN
# =============================================================================


def build_nextcloud_set_domain_steps(*, public_url: str, pull: bool) -> list[CompositeStep]:
    """
    Build the sequence of steps required to set Nextcloud domain (v2).

    Args:
        public_url: The public URL for Nextcloud (e.g., "https://cloud.example.com")
        pull: Whether to pull images before reloading the stack

    Returns:
        List of 2 CompositeStep objects:
        1. NEXTCLOUD_SET_DOMAIN - configures Nextcloud via occ commands
        2. DOCKER_RELOAD - restarts the Nextcloud stack
    """
    steps = [
        # Step 1: Configure Nextcloud domain via occ
        CompositeStep(
            type="NEXTCLOUD_SET_DOMAIN",
            payload={
                "public_url": public_url,
            },
        ),
        # Step 2: Reload Docker stack
        CompositeStep(
            type="DOCKER_RELOAD",
            payload={
                "compose_dir": NEXTCLOUD_STACK_DIR,
                "pull": pull,
            },
        ),
    ]
    return steps


async def create_nextcloud_set_domain_task(
    *,
    db: AsyncSession,
    tenant_id: uuid.UUID,
    agent_id: uuid.UUID,
    public_url: str,
    pull: bool,
) -> Task:
    """
    Create and persist a COMPOSITE task for Nextcloud set-domain.

    Args:
        db: Async database session
        tenant_id: UUID of the tenant
        agent_id: UUID of the agent to assign the task to
        public_url: The public URL for Nextcloud
        pull: Whether to pull images before reloading

    Returns:
        The created Task object with type="COMPOSITE"
    """
    steps = build_nextcloud_set_domain_steps(public_url=public_url, pull=pull)

    task = Task(
        tenant_id=tenant_id,
        agent_id=agent_id,
        type="COMPOSITE",
        payload={"steps": [step.model_dump() for step in steps]},
        status=TaskStatus.PENDING.value,
    )

    db.add(task)
    await db.commit()
    await db.refresh(task)

    return task
