"""Poste.io initial setup scenario.

Automates the first-time setup for a fresh Poste.io mail server installation.
This scenario:
1. Navigates to the Poste.io admin setup page
2. Configures the mailserver hostname
3. Creates the admin email account with a generated password
4. Returns the generated credentials for secure storage
"""

import secrets
import string
from typing import Any

from playwright.async_api import Page

from app.playwright_scenarios import register_scenario
from app.playwright_scenarios.base import BaseScenario, ScenarioOptions, ScenarioResult


def generate_secure_password(length: int = 24) -> str:
    """Generate a cryptographically secure password.

    Args:
        length: Password length (default: 24)

    Returns:
        A secure random password with mixed characters
    """
    # Use a mix of letters, digits, and safe special characters
    alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
    # Ensure at least one of each type
    password = [
        secrets.choice(string.ascii_lowercase),
        secrets.choice(string.ascii_uppercase),
        secrets.choice(string.digits),
        secrets.choice("!@#$%^&*"),
    ]
    # Fill the rest randomly
    password.extend(secrets.choice(alphabet) for _ in range(length - 4))
    # Shuffle to avoid predictable positions
    password_list = list(password)
    secrets.SystemRandom().shuffle(password_list)
    return "".join(password_list)


@register_scenario
class PosteInitialSetup(BaseScenario):
    """Automate Poste.io first-time setup wizard.

    This scenario handles the initial server configuration when
    Poste.io is freshly installed. It configures the mailserver
    hostname and creates the administrator email account.

    Required inputs:
        base_url: The Poste.io instance URL (e.g., https://mail.example.com)
        admin_email: Admin email address (e.g., admin@example.com)

    Optional inputs:
        admin_password: Password for admin account (auto-generated if not provided)
        mailserver_hostname: Override mailserver hostname (defaults to URL hostname)

    Result data:
        setup_completed: Whether initial setup was completed
        admin_email: The configured admin email address
        admin_password: The password (generated or provided) - STORE SECURELY
        mailserver_hostname: The configured hostname
        already_configured: True if Poste was already set up
    """

    @property
    def name(self) -> str:
        return "poste_initial_setup"

    @property
    def required_inputs(self) -> list[str]:
        return ["base_url", "admin_email"]

    @property
    def optional_inputs(self) -> list[str]:
        return ["admin_password", "mailserver_hostname"]

    @property
    def description(self) -> str:
        return "Automate Poste.io first-time mail server setup"

    async def execute(
        self,
        page: Page,
        inputs: dict[str, Any],
        options: ScenarioOptions,
    ) -> ScenarioResult:
        """Execute the Poste.io initial setup.

        Args:
            page: Playwright Page object
            inputs: Scenario inputs (base_url, admin_email, optional password)
            options: Scenario options

        Returns:
            ScenarioResult with setup status and credentials
        """
        base_url = inputs["base_url"].rstrip("/")
        admin_email = inputs["admin_email"]

        # Generate password if not provided
        admin_password = inputs.get("admin_password") or generate_secure_password()

        # Extract hostname from URL if not provided
        from urllib.parse import urlparse
        parsed_url = urlparse(base_url)
        mailserver_hostname = inputs.get("mailserver_hostname") or parsed_url.netloc

        screenshots = []
        result_data = {
            "setup_completed": False,
            "admin_email": admin_email,
            "admin_password": admin_password,  # Return for secure storage
            "mailserver_hostname": mailserver_hostname,
            "already_configured": False,
        }

        try:
            # Navigate to Poste.io
            await page.goto(base_url, wait_until="networkidle")

            current_url = page.url

            # Check if we're on the setup page
            if "/admin/install/server" not in current_url:
                # Check if redirected to login (already configured)
                if "/admin/login" in current_url or "/webmail" in current_url:
                    result_data["already_configured"] = True
                    result_data["setup_completed"] = True
                    return ScenarioResult(
                        success=True,
                        data=result_data,
                        screenshots=screenshots,
                        error=None,
                    )

                # Try navigating directly to setup page
                await page.goto(f"{base_url}/admin/install/server", wait_until="networkidle")

                # If still not on setup, it's already configured
                if "/admin/install/server" not in page.url:
                    result_data["already_configured"] = True
                    result_data["setup_completed"] = True
                    return ScenarioResult(
                        success=True,
                        data=result_data,
                        screenshots=screenshots,
                        error=None,
                    )

            # We're on the setup page - configure the mail server

            # Wait for the hostname input to be visible
            hostname_input = page.locator('input[placeholder*="mail.example.com"]')
            await hostname_input.wait_for(state="visible", timeout=10000)

            # Clear and fill hostname (may be pre-filled)
            await hostname_input.clear()
            await hostname_input.fill(mailserver_hostname)

            # Fill admin email
            admin_email_input = page.locator('input[placeholder*="admin@example.com"]')
            await admin_email_input.wait_for(state="visible", timeout=5000)
            await admin_email_input.fill(admin_email)

            # Fill password
            password_input = page.locator('input[type="password"], input[placeholder*="Password"]').last
            await password_input.wait_for(state="visible", timeout=5000)
            await password_input.fill(admin_password)

            # Take screenshot before submitting if requested
            if options.screenshot_on_success and options.artifacts_dir:
                pre_submit_path = options.artifacts_dir / "poste_pre_submit.png"
                await page.screenshot(path=str(pre_submit_path))
                screenshots.append(str(pre_submit_path))

            # Click Submit button
            submit_button = page.locator('button:has-text("Submit")')
            await submit_button.click()

            # Wait for setup to complete - should redirect away from install page
            try:
                await page.wait_for_url(
                    lambda url: "/admin/install" not in url,
                    timeout=60000,  # 60 seconds for setup
                )
                result_data["setup_completed"] = True
            except Exception:
                # Check if there's an error message
                error_el = page.locator('.error, .alert-danger, [class*="error"]')
                if await error_el.count() > 0:
                    error_text = await error_el.first.text_content()
                    return ScenarioResult(
                        success=False,
                        data=result_data,
                        screenshots=screenshots,
                        error=f"Setup failed: {error_text}",
                    )

                # Still on page but no error - might have succeeded
                result_data["setup_completed"] = True

            # Take final screenshot
            if options.screenshot_on_success and options.artifacts_dir:
                final_path = options.artifacts_dir / "poste_setup_complete.png"
                await page.screenshot(path=str(final_path))
                screenshots.append(str(final_path))

            return ScenarioResult(
                success=result_data["setup_completed"],
                data=result_data,
                screenshots=screenshots,
                error=None,
            )

        except Exception as e:
            # Take error screenshot
            if options.screenshot_on_failure and options.artifacts_dir:
                error_path = options.artifacts_dir / "poste_setup_error.png"
                await page.screenshot(path=str(error_path))
                screenshots.append(str(error_path))

            return ScenarioResult(
                success=False,
                data=result_data,
                screenshots=screenshots,
                error=f"Poste.io setup failed: {str(e)}",
            )
