"""
IAM API emulator.

Handles service accounts, keys, and IAM roles used by Terraform's
google_service_account, google_project_iam_*, etc.
"""

import uuid

from flask import Blueprint, jsonify, request as flask_request

from gcplocal.services.base import GCPService
from gcplocal.constants import IAM_API_PREFIX
from gcplocal.utils import error_response, gcp_id, timestamp_now

SVC = "iam"


class IAMService(GCPService):
    service_name = "iam"
    api_prefix = IAM_API_PREFIX

    def register_routes(self, bp: Blueprint) -> None:
        p = self.api_prefix

        # --- Service Accounts ---
        bp.add_url_rule(
            f"{p}/projects/<project>/serviceAccounts",
            "list_service_accounts", self.list_service_accounts, methods=["GET"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/serviceAccounts",
            "create_service_account", self.create_service_account, methods=["POST"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/serviceAccounts/<path:email>",
            "get_service_account", self.get_service_account, methods=["GET"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/serviceAccounts/<path:email>",
            "delete_service_account", self.delete_service_account, methods=["DELETE"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/serviceAccounts/<path:email>",
            "patch_service_account", self.patch_service_account, methods=["PATCH", "PUT"],
        )

        # --- Service Account Keys ---
        bp.add_url_rule(
            f"{p}/projects/<project>/serviceAccounts/<path:email>/keys",
            "list_sa_keys", self.list_sa_keys, methods=["GET"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/serviceAccounts/<path:email>/keys",
            "create_sa_key", self.create_sa_key, methods=["POST"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/serviceAccounts/<path:email>/keys/<key_id>",
            "get_sa_key", self.get_sa_key, methods=["GET"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/serviceAccounts/<path:email>/keys/<key_id>",
            "delete_sa_key", self.delete_sa_key, methods=["DELETE"],
        )

        # --- Roles ---
        bp.add_url_rule(
            f"{p}/projects/<project>/roles",
            "list_project_roles", self.list_project_roles, methods=["GET"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/roles",
            "create_project_role", self.create_project_role, methods=["POST"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/roles/<role_id>",
            "get_project_role", self.get_project_role, methods=["GET"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/roles/<role_id>",
            "delete_project_role", self.delete_project_role, methods=["DELETE"],
        )
        bp.add_url_rule(
            f"{p}/projects/<project>/roles/<role_id>",
            "patch_project_role", self.patch_project_role, methods=["PATCH"],
        )
        bp.add_url_rule(
            f"{p}/roles", "list_predefined_roles", self.list_predefined_roles, methods=["GET"],
        )

    # ── Service Accounts ───────────────────────────────────────────────

    def list_service_accounts(self, project: str):
        accounts = self.state.list(SVC, project, "serviceAccounts")
        return jsonify({"accounts": accounts})

    def create_service_account(self, project: str):
        body = flask_request.get_json(silent=True) or {}
        account_id = body.get("accountId", f"sa-{gcp_id()[:8]}")
        sa_body = body.get("serviceAccount", {})
        email = f"{account_id}@{project}.iam.gserviceaccount.com"
        unique_id = gcp_id()

        sa = {
            "name": f"projects/{project}/serviceAccounts/{email}",
            "projectId": project,
            "uniqueId": unique_id,
            "email": email,
            "displayName": sa_body.get("displayName", account_id),
            "description": sa_body.get("description", ""),
            "etag": "MOCK_ETAG",
            "disabled": False,
            "oauth2ClientId": unique_id,
        }

        self.state.put(SVC, project, "serviceAccounts", email, sa)
        return jsonify(sa)

    def get_service_account(self, project: str, email: str):
        sa = self.state.get(SVC, project, "serviceAccounts", email)
        if not sa:
            return error_response(404, f"Service account {email} not found")
        return jsonify(sa)

    def delete_service_account(self, project: str, email: str):
        if not self.state.delete(SVC, project, "serviceAccounts", email):
            return error_response(404, f"Service account {email} not found")
        return "", 200

    def patch_service_account(self, project: str, email: str):
        existing = self.state.get(SVC, project, "serviceAccounts", email)
        if not existing:
            return error_response(404, f"Service account {email} not found")
        body = flask_request.get_json(silent=True) or {}
        sa_update = body.get("serviceAccount", body)
        mask = body.get("updateMask", "")
        if mask:
            fields = [f.strip() for f in mask.split(",")]
            for field in fields:
                if field in sa_update:
                    existing[field] = sa_update[field]
        else:
            existing.update({k: v for k, v in sa_update.items() if k not in ("name", "email", "uniqueId")})
        self.state.put(SVC, project, "serviceAccounts", email, existing)
        return jsonify(existing)

    # ── Service Account Keys ───────────────────────────────────────────

    def list_sa_keys(self, project: str, email: str):
        keys = self.state.list(SVC, project, f"sa_keys/{email}")
        return jsonify({"keys": keys})

    def create_sa_key(self, project: str, email: str):
        key_id = uuid.uuid4().hex[:40]
        key = {
            "name": f"projects/{project}/serviceAccounts/{email}/keys/{key_id}",
            "privateKeyType": "TYPE_GOOGLE_CREDENTIALS_FILE",
            "keyAlgorithm": "KEY_ALG_RSA_2048",
            "privateKeyData": "REDACTED_FOR_LOCAL_EMULATION",
            "validAfterTime": timestamp_now(),
            "validBeforeTime": "2030-01-01T00:00:00Z",
            "keyOrigin": "GOOGLE_PROVIDED",
            "keyType": "USER_MANAGED",
        }
        self.state.put(SVC, project, f"sa_keys/{email}", key_id, key)
        return jsonify(key)

    def get_sa_key(self, project: str, email: str, key_id: str):
        key = self.state.get(SVC, project, f"sa_keys/{email}", key_id)
        if not key:
            return error_response(404, f"Key {key_id} not found")
        return jsonify(key)

    def delete_sa_key(self, project: str, email: str, key_id: str):
        self.state.delete(SVC, project, f"sa_keys/{email}", key_id)
        return "", 200

    # ── Roles ──────────────────────────────────────────────────────────

    def list_project_roles(self, project: str):
        roles = self.state.list(SVC, project, "roles")
        return jsonify({"roles": roles})

    def create_project_role(self, project: str):
        body = flask_request.get_json(silent=True) or {}
        role_id = body.get("roleId", f"role_{gcp_id()[:8]}")
        role = body.get("role", {})

        r = {
            "name": f"projects/{project}/roles/{role_id}",
            "title": role.get("title", role_id),
            "description": role.get("description", ""),
            "includedPermissions": role.get("includedPermissions", []),
            "stage": role.get("stage", "GA"),
            "etag": "MOCK_ETAG",
            "deleted": False,
        }
        self.state.put(SVC, project, "roles", role_id, r)
        return jsonify(r)

    def get_project_role(self, project: str, role_id: str):
        role = self.state.get(SVC, project, "roles", role_id)
        if not role:
            return error_response(404, f"Role {role_id} not found")
        return jsonify(role)

    def delete_project_role(self, project: str, role_id: str):
        role = self.state.get(SVC, project, "roles", role_id)
        if role:
            role["deleted"] = True
            self.state.put(SVC, project, "roles", role_id, role)
        return jsonify(role or {})

    def patch_project_role(self, project: str, role_id: str):
        existing = self.state.get(SVC, project, "roles", role_id)
        if not existing:
            return error_response(404, f"Role {role_id} not found")
        body = flask_request.get_json(silent=True) or {}
        existing.update({k: v for k, v in body.items() if v is not None})
        self.state.put(SVC, project, "roles", role_id, existing)
        return jsonify(existing)

    def list_predefined_roles(self):
        """Return a minimal set of common predefined roles."""
        roles = [
            {"name": "roles/viewer", "title": "Viewer", "description": "Read access to all resources"},
            {"name": "roles/editor", "title": "Editor", "description": "Edit access to all resources"},
            {"name": "roles/owner", "title": "Owner", "description": "Full access to all resources"},
            {"name": "roles/iam.serviceAccountUser", "title": "Service Account User", "description": "Run operations as the service account"},
            {"name": "roles/storage.admin", "title": "Storage Admin", "description": "Full control of GCS resources"},
            {"name": "roles/compute.admin", "title": "Compute Admin", "description": "Full control of Compute Engine resources"},
        ]
        return jsonify({"roles": roles})
