"""Deploy generator — reads ProjectSpec.deploy to generate deployment infra."""

from __future__ import annotations

from pathlib import Path

from prisme.generators.base import GeneratedFile, GeneratorBase, GeneratorResult
from prisme.spec.stack import FileStrategy


class DeployGenerator(GeneratorBase):
    """Generate deployment infrastructure from ProjectSpec.deploy."""

    def generate_files(self) -> list[GeneratedFile]:
        project_spec = self.context.project_spec
        if project_spec is None or project_spec.deploy is None:
            return []

        deploy = project_spec.deploy

        if deploy.provider != "hetzner":
            return []

        from prisme.deploy import DeploymentConfig, HetznerConfig, HetznerDeployGenerator
        from prisme.deploy.config import HetznerLocation, HetznerServerType

        hetzner = deploy.hetzner

        location_map = {
            "nbg1": HetznerLocation.NUREMBERG,
            "fsn1": HetznerLocation.FALKENSTEIN,
            "hel1": HetznerLocation.HELSINKI,
            "ash": HetznerLocation.ASHBURN,
            "hil": HetznerLocation.HILLSBORO,
        }
        server_type_map = {
            "cx22": HetznerServerType.CX22,
            "cx23": HetznerServerType.CX23,
            "cx32": HetznerServerType.CX32,
            "cx42": HetznerServerType.CX42,
            "cx52": HetznerServerType.CX52,
            "cax11": HetznerServerType.CAX11,
        }

        hetzner_config = HetznerConfig(
            location=location_map.get(hetzner.location, HetznerLocation.FALKENSTEIN),
            staging_server_type=server_type_map.get(
                hetzner.staging_server_type, HetznerServerType.CX23
            ),
            production_server_type=server_type_map.get(
                hetzner.production_server_type, HetznerServerType.CX23
            ),
            production_floating_ip=hetzner.production_floating_ip,
        )

        gen_config = project_spec.generator
        backend_output = Path(gen_config.backend_output)
        frontend_output = Path(gen_config.frontend_output)
        project_dir = Path.cwd()

        backend_path_relative = str(backend_output.parent)
        frontend_path_relative = str(
            frontend_output.parent
            if (project_dir / frontend_output.parent / "package.json").exists()
            else frontend_output
        )
        backend_module = project_spec.backend.module_name or backend_output.name

        config = DeploymentConfig(
            project_name=project_spec.name,
            domain=deploy.domain,
            ssl_email=deploy.ssl_email,
            use_redis=deploy.use_redis,
            hetzner=hetzner_config,
            backend_path=backend_path_relative,
            frontend_path=frontend_path_relative,
            backend_module=backend_module,
        )

        # Use HetznerDeployGenerator for template rendering only
        gen = HetznerDeployGenerator(project_dir, config)

        return self._collect_files(gen)

    def generate(self) -> GeneratorResult:
        """Generate files and make scripts executable."""
        result = super().generate()

        # Make deploy scripts executable (post-write)
        if not self.context.dry_run:
            for script_name in ("deploy/scripts/deploy.sh", "deploy/scripts/rollback.sh"):
                script_path = self.output_dir / script_name
                if script_path.exists():
                    script_path.chmod(0o755)

        return result

    def _collect_files(self, gen: object) -> list[GeneratedFile]:
        """Collect all deploy files as GeneratedFile objects."""
        from prisme.deploy import HetznerDeployGenerator

        assert isinstance(gen, HetznerDeployGenerator)
        ctx = gen._get_template_context()
        env = gen.env

        files: list[GeneratedFile] = []

        def render(template_name: str) -> str:
            template = env.get_template(template_name)
            return template.render(**ctx)

        # Terraform files
        tf_files = [
            ("hetzner/terraform/main.tf.jinja2", "deploy/terraform/main.tf"),
            ("hetzner/terraform/variables.tf.jinja2", "deploy/terraform/variables.tf"),
            ("hetzner/terraform/outputs.tf.jinja2", "deploy/terraform/outputs.tf"),
            ("hetzner/terraform/versions.tf.jinja2", "deploy/terraform/versions.tf"),
            ("hetzner/terraform/staging.tfvars.jinja2", "deploy/terraform/staging.tfvars"),
            ("hetzner/terraform/production.tfvars.jinja2", "deploy/terraform/production.tfvars"),
        ]
        for template_name, output_path in tf_files:
            files.append(
                GeneratedFile(
                    path=Path(output_path),
                    content=render(template_name),
                    strategy=FileStrategy.GENERATE_ONCE,
                    description=f"Terraform {Path(output_path).name}",
                )
            )

        # Terraform modules
        modules = {
            "server": ["main.tf", "variables.tf", "outputs.tf"],
            "volume": ["main.tf", "variables.tf", "outputs.tf"],
        }
        for module, module_files in modules.items():
            for filename in module_files:
                template = f"hetzner/terraform/modules/{module}/{filename}.jinja2"
                output = f"deploy/terraform/modules/{module}/{filename}"
                files.append(
                    GeneratedFile(
                        path=Path(output),
                        content=render(template),
                        strategy=FileStrategy.GENERATE_ONCE,
                        description=f"Terraform module {module}/{filename}",
                    )
                )

        # Cloud init
        files.append(
            GeneratedFile(
                path=Path("deploy/terraform/cloud-init/user-data.yml"),
                content=render("hetzner/cloud-init/user-data.yml.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Cloud init config",
            )
        )

        # Environment templates
        files.append(
            GeneratedFile(
                path=Path("deploy/env/.env.staging.template"),
                content=render("env/.env.staging.template.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Staging env template",
            )
        )
        files.append(
            GeneratedFile(
                path=Path("deploy/env/.env.production.template"),
                content=render("env/.env.production.template.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Production env template",
            )
        )

        # Scripts
        files.append(
            GeneratedFile(
                path=Path("deploy/scripts/deploy.sh"),
                content=render("hetzner/scripts/deploy.sh.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Deploy script",
            )
        )
        files.append(
            GeneratedFile(
                path=Path("deploy/scripts/rollback.sh"),
                content=render("hetzner/scripts/rollback.sh.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Rollback script",
            )
        )

        # README
        files.append(
            GeneratedFile(
                path=Path("deploy/README.md"),
                content=render("README.md.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Deploy README",
            )
        )

        # GitHub workflows
        files.append(
            GeneratedFile(
                path=Path(".github/workflows/deploy.yml"),
                content=render("github/deploy.yml.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Deploy workflow",
            )
        )
        files.append(
            GeneratedFile(
                path=Path(".github/workflows/terraform.yml"),
                content=render("github/terraform.yml.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Terraform workflow",
            )
        )

        # Traefik config
        files.append(
            GeneratedFile(
                path=Path("deploy/traefik/traefik.yml"),
                content=render("traefik/traefik.yml.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Traefik config",
            )
        )
        files.append(
            GeneratedFile(
                path=Path("deploy/traefik/dynamic/infrastructure.yml"),
                content=render("traefik/dynamic/infrastructure.yml.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Traefik infrastructure config",
            )
        )
        files.append(
            GeneratedFile(
                path=Path("deploy/traefik/dynamic/middlewares.yml"),
                content=render("traefik/dynamic/middlewares.yml.jinja2"),
                strategy=FileStrategy.GENERATE_ONCE,
                description="Traefik middlewares config",
            )
        )

        return files
