"""Billing management for AWS account cost passthrough via Stripe."""

import json
import os
from datetime import datetime
from typing import Any

from botocore.exceptions import ClientError

from ai_org.core.aws_client import AWSClient


class BillingManager:
    """Manages billing operations for AWS accounts via Stripe."""

    def __init__(self, aws_client: AWSClient):
        """Initialize billing manager.

        Args:
            aws_client: AWS client instance for AWS operations
        """
        self.aws_client = aws_client
        self.dynamodb = aws_client.client("dynamodb")
        self.secrets_client = aws_client.client("secretsmanager")
        self.table_name = "stripe-customer-mappings"

    @property
    def account_id(self) -> str:
        """Get the current AWS account ID."""
        sts = self.aws_client.client("sts")
        return sts.get_caller_identity()["Account"]

    def _get_stripe_api_key(self) -> str:
        """Retrieve Stripe API key from Secrets Manager or environment.

        Prefers TEST_STRIPE_SECRET_KEY for testing, then falls back to
        production keys from Secrets Manager or STRIPE_SECRET_KEY env var.

        Returns:
            Stripe API key

        Raises:
            Exception if API key cannot be retrieved
        """
        # First check for test key in environment (preferred for testing)
        test_key = os.getenv("TEST_STRIPE_SECRET_KEY")
        if test_key:
            return test_key

        # Then try Secrets Manager for production
        try:
            response = self.secrets_client.get_secret_value(SecretId="stripe-api-key")
            secret = json.loads(response["SecretString"])
            return secret["api_key"]
        except ClientError as e:
            if e.response["Error"]["Code"] == "ResourceNotFoundException":
                # Fall back to production environment variable
                api_key = os.getenv("STRIPE_SECRET_KEY")
                if not api_key:
                    raise Exception(
                        "Stripe API key not found. Please set TEST_STRIPE_SECRET_KEY (for testing) "
                        "or STRIPE_SECRET_KEY environment variable, or create 'stripe-api-key' secret "
                        "in AWS Secrets Manager"
                    )
                return api_key
            raise

    def setup_stripe_products(self) -> dict[str, str]:
        """Create Stripe products and prices for AWS billing.

        Returns:
            Dictionary with product_id and price_id

        Raises:
            Exception if Stripe setup fails
        """
        # Import stripe here to avoid initialization issues
        import stripe

        stripe.api_key = self._get_stripe_api_key()

        try:
            # Create product
            product = stripe.Product.create(
                name="AWS Infrastructure Services",
                description="AWS cloud infrastructure and services with management",
            )

            # Create price (monthly recurring with usage-based billing)
            price = stripe.Price.create(
                product=product.id,
                nickname="AWS Infrastructure",
                currency="usd",
                recurring={"interval": "month"},
                billing_scheme="tiered",
                tiers_mode="graduated",
                tiers=[
                    {"up_to": "inf", "unit_amount": 120}  # $1.20 per dollar (20% markup)
                ],
                transform_quantity={"divide_by": 100, "round": "up"},
            )

            return {"product_id": product.id, "price_id": price.id}

        except stripe.error.StripeError as e:
            raise Exception(f"Stripe API error: {e}")

    def link_account(self, account_id: str, stripe_customer_id: str) -> None:
        """Link AWS account to Stripe customer.

        Args:
            account_id: AWS account ID
            stripe_customer_id: Stripe customer ID

        Raises:
            Exception if linking fails
        """
        try:
            # Check if customer already exists
            response = self.dynamodb.get_item(
                TableName=self.table_name,
                Key={"stripe_customer_id": {"S": stripe_customer_id}},
            )

            if "Item" in response:
                # Update existing customer - add account to list
                current_accounts = response["Item"].get("aws_accounts", {}).get("L", [])
                account_list = [item["S"] for item in current_accounts]

                if account_id not in account_list:
                    account_list.append(account_id)

                self.dynamodb.update_item(
                    TableName=self.table_name,
                    Key={"stripe_customer_id": {"S": stripe_customer_id}},
                    UpdateExpression="SET aws_accounts = :accounts, updated_at = :updated",
                    ExpressionAttributeValues={
                        ":accounts": {"L": [{"S": acc} for acc in account_list]},
                        ":updated": {"S": datetime.now().isoformat()},
                    },
                )
            else:
                # Create new customer mapping
                self.dynamodb.put_item(
                    TableName=self.table_name,
                    Item={
                        "stripe_customer_id": {"S": stripe_customer_id},
                        "aws_accounts": {"L": [{"S": account_id}]},
                        "created_at": {"S": datetime.now().isoformat()},
                    },
                )

        except ClientError as e:
            if e.response["Error"]["Code"] == "ResourceNotFoundException":
                raise Exception(
                    f"DynamoDB table '{self.table_name}' not found. "
                    "Please deploy billing infrastructure first."
                )
            raise Exception(f"Failed to link account: {e}")

    def unlink_account(self, account_id: str) -> None:
        """Remove AWS account from billing.

        Args:
            account_id: AWS account ID to unlink

        Raises:
            Exception if unlinking fails
        """
        try:
            # Scan table to find customer with this account
            response = self.dynamodb.scan(TableName=self.table_name)

            for item in response.get("Items", []):
                customer_id = item["stripe_customer_id"]["S"]
                accounts = [acc["S"] for acc in item.get("aws_accounts", {}).get("L", [])]

                if account_id in accounts:
                    accounts.remove(account_id)

                    if accounts:
                        # Update with remaining accounts
                        self.dynamodb.update_item(
                            TableName=self.table_name,
                            Key={"stripe_customer_id": {"S": customer_id}},
                            UpdateExpression="SET aws_accounts = :accounts, updated_at = :updated",
                            ExpressionAttributeValues={
                                ":accounts": {"L": [{"S": acc} for acc in accounts]},
                                ":updated": {"S": datetime.now().isoformat()},
                            },
                        )
                    else:
                        # No accounts left, delete the mapping
                        self.dynamodb.delete_item(
                            TableName=self.table_name,
                            Key={"stripe_customer_id": {"S": customer_id}},
                        )
                    return

            raise Exception(f"Account {account_id} not found in any billing mappings")

        except ClientError as e:
            if e.response["Error"]["Code"] == "ResourceNotFoundException":
                raise Exception(
                    f"DynamoDB table '{self.table_name}' not found. "
                    "Please deploy billing infrastructure first."
                )
            raise Exception(f"Failed to unlink account: {e}")

    def list_mappings(self) -> list[dict[str, Any]]:
        """List all AWS account to Stripe customer mappings.

        Returns:
            List of mapping dictionaries

        Raises:
            Exception if listing fails
        """
        try:
            response = self.dynamodb.scan(TableName=self.table_name)

            mappings = []
            for item in response.get("Items", []):
                mapping = {
                    "stripe_customer_id": item["stripe_customer_id"]["S"],
                    "aws_accounts": [acc["S"] for acc in item.get("aws_accounts", {}).get("L", [])],
                }

                # Add timestamps if present
                if "created_at" in item:
                    mapping["created_at"] = item["created_at"]["S"]
                if "updated_at" in item:
                    mapping["updated_at"] = item["updated_at"]["S"]

                mappings.append(mapping)

            return sorted(mappings, key=lambda x: x.get("created_at", ""))

        except ClientError as e:
            if e.response["Error"]["Code"] == "ResourceNotFoundException":
                raise Exception(
                    f"DynamoDB table '{self.table_name}' not found. "
                    "Please deploy billing infrastructure first."
                )
            raise Exception(f"Failed to list mappings: {e}")
