"""AWS Resource Explorer 2 client."""

from dataclasses import dataclass

import boto3
import pandas as pd
from rich.console import Console

from .getter import get_tags

console = Console()

TAG_COLUMNS = ["Workload", "Name"]


@dataclass
class ResourceOptions:
    taggable: bool
    tracked: bool
    billed: bool


class Resources:
    # https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_supported-resources-enforcement.html
    OPTIONS: dict[str, ResourceOptions] = {
        "acm:certificate": ResourceOptions(taggable=True, tracked=True, billed=False),
        "apigateway:restapis": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "apigateway:restapis/deployments": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "apigateway:restapis/resources": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "apigateway:restapis/resources/methods": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "apigateway:restapis/stages": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "apprunner:autoscalingconfiguration": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "athena:datacatalog": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "athena:workgroup": ResourceOptions(taggable=True, tracked=True, billed=True),
        "autoscaling:autoScalingGroup": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),  # TODO: use autoscaling:CreateOrUpdateTags
        "backup:backup-plan": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "backup:backup-vault": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "ce:anomalymonitor": ResourceOptions(taggable=True, tracked=False, billed=True),
        "ce:anomalysubscription": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "cloudformation:stack": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),
        "cloudfront:cache-policy": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "cloudfront:distribution": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "cloudfront:function": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "cloudfront:origin-access-control": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "cloudfront:origin-access-identity": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "cloudtrail:trail": ResourceOptions(taggable=True, tracked=True, billed=False),
        "cloudwatch:alarm": ResourceOptions(taggable=True, tracked=True, billed=True),
        "cloudwatch:dashboard": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "codebuild:project": ResourceOptions(taggable=True, tracked=True, billed=True),
        "codecommit:repository": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "codedeploy:application": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "codepipeline:pipeline": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "datasync:location": ResourceOptions(taggable=True, tracked=False, billed=True),
        "datasync:task": ResourceOptions(taggable=True, tracked=True, billed=True),
        "dms:subgrp": ResourceOptions(taggable=True, tracked=False, billed=True),
        "dynamodb:table": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ec2:dhcp-options": ResourceOptions(taggable=True, tracked=False, billed=True),
        "ec2:elastic-ip": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ec2:fleet": ResourceOptions(taggable=True, tracked=False, billed=True),
        "ec2:image": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ec2:instance": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ec2:internet-gateway": ResourceOptions(
            taggable=True, tracked=False, billed=False
        ),
        "ec2:key-pair": ResourceOptions(taggable=True, tracked=False, billed=True),
        "ec2:launch-template": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),
        "ec2:natgateway": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ec2:network-acl": ResourceOptions(taggable=True, tracked=False, billed=True),
        "ec2:network-interface": ResourceOptions(
            taggable=True, tracked=False, billed=False
        ),
        "ec2:placement-group": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "ec2:reserved-instances": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),
        "ec2:route-table": ResourceOptions(taggable=True, tracked=False, billed=False),
        "ec2:security-group": ResourceOptions(
            taggable=True, tracked=False, billed=False
        ),
        "ec2:security-group-rule": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "ec2:snapshot": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ec2:spot-instances-request": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),
        "ec2:subnet": ResourceOptions(taggable=True, tracked=False, billed=False),
        "ec2:volume": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ec2:vpc": ResourceOptions(taggable=True, tracked=False, billed=False),
        "ec2:vpc-endpoint": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ec2:vpc-peering-connection": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "ecr:repository": ResourceOptions(taggable=True, tracked=True, billed=True),
        "eks:cluster": ResourceOptions(taggable=True, tracked=True, billed=True),
        "eks:daemonset": ResourceOptions(taggable=False, tracked=False, billed=True),
        "eks:deployment": ResourceOptions(taggable=False, tracked=False, billed=True),
        "eks:endpointslice": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "eks:ingress": ResourceOptions(taggable=False, tracked=False, billed=True),
        "eks:namespace": ResourceOptions(taggable=False, tracked=False, billed=True),
        "eks:persistentvolume": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "eks:replicaset": ResourceOptions(taggable=False, tracked=False, billed=True),
        "eks:service": ResourceOptions(taggable=False, tracked=False, billed=True),
        "eks:statefulset": ResourceOptions(taggable=False, tracked=False, billed=True),
        "elasticache:cluster": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "elasticache:parametergroup": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),
        "elasticache:replicationgroup": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "elasticache:reserved-instance": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),
        "elasticache:subnetgroup": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),
        "elasticache:user": ResourceOptions(taggable=True, tracked=True, billed=False),
        "elasticfilesystem:file-system": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "elasticloadbalancing:listener-rule/app": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "elasticloadbalancing:listener/app": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "elasticloadbalancing:listener/net": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "elasticloadbalancing:loadbalancer": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "elasticloadbalancing:loadbalancer/app": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "elasticloadbalancing:loadbalancer/net": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "elasticloadbalancing:targetgroup": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),
        "es:domain": ResourceOptions(taggable=True, tracked=True, billed=True),
        "events:event-bus": ResourceOptions(taggable=True, tracked=True, billed=True),
        "events:rule": ResourceOptions(taggable=True, tracked=True, billed=False),
        "glue:database": ResourceOptions(taggable=True, tracked=False, billed=True),
        "glue:table": ResourceOptions(taggable=False, tracked=False, billed=True),
        "guardduty:detector": ResourceOptions(taggable=True, tracked=True, billed=True),
        "iam:group": ResourceOptions(taggable=False, tracked=False, billed=True),
        "iam:instance-profile": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "iam:mfa": ResourceOptions(taggable=True, tracked=False, billed=True),
        "iam:oidc-provider": ResourceOptions(taggable=True, tracked=False, billed=True),
        "iam:policy": ResourceOptions(taggable=True, tracked=False, billed=True),
        "iam:role": ResourceOptions(taggable=True, tracked=False, billed=True),
        "iam:server-certificate": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "iam:user": ResourceOptions(taggable=True, tracked=False, billed=True),
        "inspector:target/template": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "kms:key": ResourceOptions(taggable=True, tracked=True, billed=False),
        "lambda:function": ResourceOptions(taggable=True, tracked=True, billed=True),
        "logs:log-group": ResourceOptions(taggable=True, tracked=True, billed=True),
        "memorydb:acl": ResourceOptions(taggable=True, tracked=False, billed=True),
        "memorydb:parametergroup": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "memorydb:user": ResourceOptions(taggable=True, tracked=False, billed=True),
        "mq:configuration": ResourceOptions(taggable=True, tracked=False, billed=True),
        "rds:auto-backup": ResourceOptions(taggable=False, tracked=False, billed=True),
        "rds:cluster": ResourceOptions(taggable=True, tracked=True, billed=True),
        "rds:cluster-pg": ResourceOptions(taggable=True, tracked=False, billed=True),
        "rds:cluster-snapshot": ResourceOptions(
            taggable=True, tracked=True, billed=False
        ),
        "rds:db": ResourceOptions(taggable=True, tracked=True, billed=True),
        "rds:deployment": ResourceOptions(taggable=True, tracked=False, billed=True),
        "rds:og": ResourceOptions(taggable=True, tracked=False, billed=True),
        "rds:pg": ResourceOptions(taggable=True, tracked=False, billed=True),
        "rds:ri": ResourceOptions(taggable=True, tracked=True, billed=False),
        "rds:secgrp": ResourceOptions(taggable=True, tracked=False, billed=True),
        "rds:snapshot": ResourceOptions(taggable=True, tracked=True, billed=False),
        "rds:subgrp": ResourceOptions(taggable=True, tracked=False, billed=True),
        "resource-explorer-2:index": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "resource-explorer-2:view": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "resource-groups:group": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "route53:healthcheck": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "route53:hostedzone": ResourceOptions(taggable=True, tracked=True, billed=True),
        "route53resolver:resolver-query-log-config": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "s3:bucket": ResourceOptions(taggable=True, tracked=True, billed=True),
        "s3:storage-lens": ResourceOptions(taggable=True, tracked=False, billed=True),
        "secretsmanager:secret": ResourceOptions(
            taggable=True, tracked=True, billed=True
        ),
        "ses:configuration-set": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "ses:identity": ResourceOptions(taggable=True, tracked=False, billed=True),
        "sns:topic": ResourceOptions(taggable=True, tracked=True, billed=True),
        "sqs:queue": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ssm:association": ResourceOptions(taggable=True, tracked=False, billed=True),
        "ssm:document": ResourceOptions(taggable=True, tracked=False, billed=True),
        "ssm:managed-instance": ResourceOptions(
            taggable=True, tracked=False, billed=True
        ),
        "ssm:parameter": ResourceOptions(taggable=True, tracked=True, billed=True),
        "ssm:resource-data-sync": ResourceOptions(
            taggable=False, tracked=False, billed=True
        ),
        "ssm:session": ResourceOptions(taggable=False, tracked=False, billed=True),
        "wafv2:ipset": ResourceOptions(taggable=True, tracked=False, billed=True),
        "wafv2:rulegroup": ResourceOptions(taggable=True, tracked=False, billed=True),
        "wafv2:webacl": ResourceOptions(taggable=True, tracked=True, billed=True),
    }

    @classmethod
    def fetch(cls, session: boto3.Session) -> list[dict]:
        """Fetch all resources via Resource Explorer 2."""
        client = session.client("resource-explorer-2")
        paginator = client.get_paginator("list_resources")

        resources = []
        for page in paginator.paginate():
            resources.extend(page["Resources"])
            console.print(f"Fetched {len(resources)} resources...")
        return resources

    @classmethod
    def get(cls) -> list[dict]:
        """Fetch all resources with default session."""
        session = boto3.Session()
        return cls.fetch(session)

    @classmethod
    def to_dataframe(cls, raw: list[dict], billed: bool, tags: list[str]) -> pd.DataFrame:
        """Convert resources to DataFrame with tag columns."""
        resources = []
        for r in raw:
            rt = r["ResourceType"]
            opts = cls.OPTIONS[rt]

            if not opts.taggable:
                continue
            if not opts.tracked:
                continue
            if billed and not opts.billed:
                continue
            resources.append(r)

        current = {}
        types = {r["Arn"]: r["ResourceType"] for r in resources}
        if types:
            current = get_tags(types, 100)

        rows = []
        for r in resources:
            arn = r["Arn"]
            rtags = current.get(arn, {})
            row = {
                "resource_type": r["ResourceType"],
                "resource_arn": arn,
            }
            for tag in tags:
                row[f"tag:{tag}"] = rtags.get(tag, "")
            rows.append(row)

        return pd.DataFrame(rows).sort_values("resource_arn").reset_index(drop=True)
