from __future__ import annotations

import uuid
from typing import TYPE_CHECKING, Optional

from mockmesh.interceptors.common import parse_json_body
from mockmesh.interceptors.sql.common import (
    default_port,
    default_version,
    rds_endpoint            as _endpoint,
    rds_instance_arn        as _instance_arn,
    rds_cluster_arn         as _cluster_arn,
    rds_snapshot_arn        as _snap_arn,
    rds_param_group_arn     as _pg_arn,
    rds_cluster_param_group_arn as _cpg_arn,
    rds_subnet_group_arn    as _sg_arn,
    rds_option_group_arn    as _og_arn,
    rds_event_subscription_arn as _es_arn,
    rds_proxy_arn           as _proxy_arn,
    rds_cluster_endpoint    as _cluster_ep,
    rds_cluster_reader_endpoint as _cluster_reader_ep,
    rds_proxy_endpoint      as _proxy_ep,
)

if TYPE_CHECKING:
    from mockmesh.storage.manager import StorageManager


def handle_rds(sm: "StorageManager", req: dict, operation: str) -> Optional[dict]:  # noqa: C901
    bd = parse_json_body(req)

    if operation == "CreateDBInstance":
        ident   = bd.get("DBInstanceIdentifier", "mock-db")
        engine  = bd.get("Engine", "mysql")
        version = bd.get("EngineVersion") or default_version(engine)
        inst = {
            "DBInstanceIdentifier": ident,
            "DBInstanceClass":      bd.get("DBInstanceClass", "db.t3.micro"),
            "Engine":               engine,
            "EngineVersion":        version,
            "DBInstanceStatus":     "available",
            "MasterUsername":       bd.get("MasterUsername", "admin"),
            "DBName":               bd.get("DBName", ""),
            "Endpoint":             _endpoint(ident, engine, bd.get("Port")),
            "AllocatedStorage":     bd.get("AllocatedStorage", 20),
            "StorageType":          bd.get("StorageType", "gp2"),
            "MultiAZ":              bd.get("MultiAZ", False),
            "AutoMinorVersionUpgrade": bd.get("AutoMinorVersionUpgrade", True),
            "PubliclyAccessible":   bd.get("PubliclyAccessible", False),
            "StorageEncrypted":     bd.get("StorageEncrypted", False),
            "DBClusterIdentifier":  bd.get("DBClusterIdentifier"),
            "TagList":              bd.get("Tags", []),
            "DBInstanceArn":        _instance_arn(ident),
        }
        sm.rds_instance_put(ident, inst)
        return {"DBInstance": inst}

    if operation == "DescribeDBInstances":
        ident = bd.get("DBInstanceIdentifier")
        if ident:
            inst = sm.rds_instance_get(ident)
            return {"DBInstances": [inst]} if inst else {"DBInstances": []}
        return {"DBInstances": sm.rds_instance_list(), "Marker": None}

    if operation == "ModifyDBInstance":
        ident = bd.get("DBInstanceIdentifier", "")
        inst  = sm.rds_instance_get(ident) or {}
        for field, key in [
            ("DBInstanceClass",   "DBInstanceClass"),
            ("EngineVersion",     "EngineVersion"),
            ("AllocatedStorage",  "AllocatedStorage"),
            ("MultiAZ",           "MultiAZ"),
            ("StorageType",       "StorageType"),
        ]:
            if bd.get(field) is not None:
                inst[key] = bd[field]
        inst["DBInstanceStatus"] = "available"
        sm.rds_instance_put(ident, inst)
        return {"DBInstance": inst}

    if operation == "DeleteDBInstance":
        ident = bd.get("DBInstanceIdentifier", "")
        inst  = sm.rds_instance_get(ident) or {"DBInstanceIdentifier": ident}
        inst["DBInstanceStatus"] = "deleting"
        sm.rds_instance_delete(ident)
        return {"DBInstance": inst}

    if operation in ("StopDBInstance", "StartDBInstance", "RebootDBInstance"):
        ident  = bd.get("DBInstanceIdentifier", "")
        inst   = sm.rds_instance_get(ident) or {"DBInstanceIdentifier": ident}
        status = {"StopDBInstance": "stopped",
                  "StartDBInstance": "available",
                  "RebootDBInstance": "available"}[operation]
        inst["DBInstanceStatus"] = status
        sm.rds_instance_put(ident, inst)
        return {"DBInstance": inst}

    if operation == "RestoreDBInstanceFromDBSnapshot":
        ident  = bd.get("DBInstanceIdentifier", "mock-restored")
        snap   = sm.rds_snapshot_get(bd.get("DBSnapshotIdentifier", "")) or {}
        engine = snap.get("Engine", bd.get("Engine", "mysql"))
        inst   = {
            "DBInstanceIdentifier": ident,
            "DBInstanceClass":      bd.get("DBInstanceClass", snap.get("DBInstanceClass", "db.t3.micro")),
            "Engine":               engine,
            "EngineVersion":        snap.get("EngineVersion", default_version(engine)),
            "DBInstanceStatus":     "available",
            "Endpoint":             _endpoint(ident, engine),
            "AllocatedStorage":     snap.get("AllocatedStorage", 20),
            "StorageType":          snap.get("StorageType", "gp2"),
            "DBInstanceArn":        _instance_arn(ident),
        }
        sm.rds_instance_put(ident, inst)
        return {"DBInstance": inst}

    if operation == "RestoreDBInstanceToPointInTime":
        ident  = bd.get("TargetDBInstanceIdentifier", "mock-pit-restore")
        source = sm.rds_instance_get(bd.get("SourceDBInstanceIdentifier", "")) or {}
        engine = source.get("Engine", "mysql")
        inst   = {
            "DBInstanceIdentifier": ident,
            "DBInstanceClass":      source.get("DBInstanceClass", "db.t3.micro"),
            "Engine":               engine,
            "EngineVersion":        source.get("EngineVersion", default_version(engine)),
            "DBInstanceStatus":     "available",
            "Endpoint":             _endpoint(ident, engine),
            "AllocatedStorage":     source.get("AllocatedStorage", 20),
            "StorageType":          source.get("StorageType", "gp2"),
            "DBInstanceArn":        _instance_arn(ident),
        }
        sm.rds_instance_put(ident, inst)
        return {"DBInstance": inst}

    if operation == "CreateDBCluster":
        ident   = bd.get("DBClusterIdentifier", "mock-cluster")
        engine  = bd.get("Engine", "aurora-mysql")
        version = bd.get("EngineVersion") or default_version(engine)
        cluster = {
            "DBClusterIdentifier":        ident,
            "Engine":                     engine,
            "EngineVersion":              version,
            "EngineMode":                 bd.get("EngineMode", "provisioned"),
            "Status":                     "available",
            "MasterUsername":             bd.get("MasterUsername", "admin"),
            "DatabaseName":               bd.get("DatabaseName", ""),
            "DBClusterArn":               _cluster_arn(ident),
            "Endpoint":                   _cluster_ep(ident),
            "ReaderEndpoint":             _cluster_reader_ep(ident),
            "Port":                       default_port(engine),
            "MultiAZ":                    bd.get("MultiAZ", False),
            "StorageEncrypted":           bd.get("StorageEncrypted", False),
            "BackupRetentionPeriod":      bd.get("BackupRetentionPeriod", 1),
            "DBClusterMembers":           [],
            "VpcSecurityGroups":          [],
            "TagList":                    bd.get("Tags", []),
            "ServerlessV2ScalingConfiguration": bd.get("ServerlessV2ScalingConfiguration"),
        }
        sm.rds_cluster_put(ident, cluster)
        return {"DBCluster": cluster}

    if operation == "DescribeDBClusters":
        ident = bd.get("DBClusterIdentifier")
        if ident:
            cluster = sm.rds_cluster_get(ident)
            return {"DBClusters": [cluster]} if cluster else {"DBClusters": []}
        return {"DBClusters": sm.rds_cluster_list(), "Marker": None}

    if operation == "ModifyDBCluster":
        ident   = bd.get("DBClusterIdentifier", "")
        cluster = sm.rds_cluster_get(ident) or {}
        for field in ("EngineVersion", "BackupRetentionPeriod",
                      "MasterUserPassword", "NewDBClusterIdentifier"):
            if bd.get(field) is not None:
                cluster[field] = bd[field]
        cluster["Status"] = "available"
        sm.rds_cluster_put(ident, cluster)
        return {"DBCluster": cluster}

    if operation == "DeleteDBCluster":
        ident   = bd.get("DBClusterIdentifier", "")
        cluster = sm.rds_cluster_get(ident) or {"DBClusterIdentifier": ident}
        cluster["Status"] = "deleting"
        sm.rds_cluster_delete(ident)
        return {"DBCluster": cluster}

    if operation == "FailoverDBCluster":
        ident   = bd.get("DBClusterIdentifier", "")
        cluster = sm.rds_cluster_get(ident) or {"DBClusterIdentifier": ident}
        cluster["Status"] = "available"
        sm.rds_cluster_put(ident, cluster)
        return {"DBCluster": cluster}

    if operation == "RestoreDBClusterFromSnapshot":
        ident   = bd.get("DBClusterIdentifier", "mock-cluster-restored")
        engine  = bd.get("Engine", "aurora-mysql")
        cluster = {
            "DBClusterIdentifier": ident,
            "Engine":              engine,
            "EngineVersion":       bd.get("EngineVersion") or default_version(engine),
            "Status":              "available",
            "DBClusterArn":        _cluster_arn(ident),
            "Endpoint":            _cluster_ep(ident),
            "ReaderEndpoint":      _cluster_reader_ep(ident),
            "Port":                default_port(engine),
        }
        sm.rds_cluster_put(ident, cluster)
        return {"DBCluster": cluster}

    if operation == "CreateDBClusterSnapshot":
        snap_id  = bd.get("DBClusterSnapshotIdentifier", "mock-cluster-snap")
        cluster  = sm.rds_cluster_get(bd.get("DBClusterIdentifier", "")) or {}
        snap = {
            "DBClusterSnapshotIdentifier": snap_id,
            "DBClusterIdentifier":         bd.get("DBClusterIdentifier", ""),
            "Engine":                      cluster.get("Engine", "aurora-mysql"),
            "EngineVersion":               cluster.get("EngineVersion",
                                                        default_version("aurora-mysql")),
            "Status":                      "available",
            "SnapshotType":                "manual",
            "DBClusterSnapshotArn":        _snap_arn(snap_id),
            "TagList":                     bd.get("Tags", []),
        }
        sm.rds_snapshot_put(snap_id, snap)
        return {"DBClusterSnapshot": snap}

    if operation == "DescribeDBClusterSnapshots":
        snap_id = bd.get("DBClusterSnapshotIdentifier")
        if snap_id:
            snap = sm.rds_snapshot_get(snap_id)
            return {"DBClusterSnapshots": [snap]} if snap else {"DBClusterSnapshots": []}
        return {"DBClusterSnapshots": sm.rds_snapshot_list(prefix="cluster-"), "Marker": None}

    if operation == "CreateDBSnapshot":
        snap_id = bd.get("DBSnapshotIdentifier", "mock-snap")
        inst    = sm.rds_instance_get(bd.get("DBInstanceIdentifier", "")) or {}
        snap = {
            "DBSnapshotIdentifier": snap_id,
            "DBInstanceIdentifier": bd.get("DBInstanceIdentifier", ""),
            "Engine":               inst.get("Engine", "mysql"),
            "EngineVersion":        inst.get("EngineVersion", default_version("mysql")),
            "DBInstanceClass":      inst.get("DBInstanceClass", "db.t3.micro"),
            "AllocatedStorage":     inst.get("AllocatedStorage", 20),
            "StorageType":          inst.get("StorageType", "gp2"),
            "Status":               "available",
            "SnapshotType":         "manual",
            "DBSnapshotArn":        _snap_arn(snap_id),
            "TagList":              bd.get("Tags", []),
        }
        sm.rds_snapshot_put(snap_id, snap)
        return {"DBSnapshot": snap}

    if operation == "DescribeDBSnapshots":
        snap_id = bd.get("DBSnapshotIdentifier")
        if snap_id:
            snap = sm.rds_snapshot_get(snap_id)
            return {"DBSnapshots": [snap]} if snap else {"DBSnapshots": []}
        db_ident = bd.get("DBInstanceIdentifier")
        snaps = sm.rds_snapshot_list()
        if db_ident:
            snaps = [s for s in snaps if s.get("DBInstanceIdentifier") == db_ident]
        return {"DBSnapshots": snaps, "Marker": None}

    if operation == "DeleteDBSnapshot":
        snap_id = bd.get("DBSnapshotIdentifier", "")
        snap    = sm.rds_snapshot_get(snap_id) or {"DBSnapshotIdentifier": snap_id}
        snap["Status"] = "deleted"
        sm.rds_snapshot_delete(snap_id)
        return {"DBSnapshot": snap}

    if operation == "CopyDBSnapshot":
        new_id = bd.get("TargetDBSnapshotIdentifier", "mock-snap-copy")
        src    = sm.rds_snapshot_get(bd.get("SourceDBSnapshotIdentifier", "")) or {}
        snap   = {**src, "DBSnapshotIdentifier": new_id,
                  "DBSnapshotArn": _snap_arn(new_id), "Status": "available"}
        sm.rds_snapshot_put(new_id, snap)
        return {"DBSnapshot": snap}

    if operation == "CreateDBParameterGroup":
        name = bd.get("DBParameterGroupName", "mock-pg")
        pg   = {
            "DBParameterGroupName":   name,
            "DBParameterGroupFamily": bd.get("DBParameterGroupFamily", "mysql8.0"),
            "Description":            bd.get("Description", "MockMesh parameter group"),
            "DBParameterGroupArn":    _pg_arn(name),
        }
        sm.rds_param_group_put(name, pg)
        return {"DBParameterGroup": pg}

    if operation == "DescribeDBParameterGroups":
        name = bd.get("DBParameterGroupName")
        if name:
            pg = sm.rds_param_group_get(name)
            return {"DBParameterGroups": [pg]} if pg else {"DBParameterGroups": []}
        return {"DBParameterGroups": sm.rds_param_group_list(), "Marker": None}

    if operation == "ModifyDBParameterGroup":
        name   = bd.get("DBParameterGroupName", "")
        pg     = sm.rds_param_group_get(name) or {}
        params = bd.get("Parameters", [])
        existing = pg.get("_parameters", [])
        names_set = {p.get("ParameterName") for p in params}
        merged = [p for p in existing if p.get("ParameterName") not in names_set] + params
        pg["_parameters"] = merged
        sm.rds_param_group_put(name, pg)
        return {"DBParameterGroupName": name}

    if operation == "DescribeDBParameters":
        name   = bd.get("DBParameterGroupName", "")
        pg     = sm.rds_param_group_get(name) or {}
        return {"Parameters": pg.get("_parameters", []), "Marker": None}

    if operation == "ResetDBParameterGroup":
        name = bd.get("DBParameterGroupName", "")
        pg   = sm.rds_param_group_get(name) or {}
        pg["_parameters"] = []
        sm.rds_param_group_put(name, pg)
        return {"DBParameterGroupName": name}

    if operation == "DeleteDBParameterGroup":
        sm.rds_param_group_delete(bd.get("DBParameterGroupName", ""))
        return {}

    if operation == "CreateDBClusterParameterGroup":
        name = bd.get("DBClusterParameterGroupName", "mock-cpg")
        pg   = {
            "DBClusterParameterGroupName": name,
            "DBParameterGroupFamily":      bd.get("DBParameterGroupFamily", "aurora-mysql8.0"),
            "Description":                 bd.get("Description", "MockMesh cluster parameter group"),
            "DBClusterParameterGroupArn":  _cpg_arn(name),
        }
        sm.rds_param_group_put(f"cluster-{name}", pg)
        return {"DBClusterParameterGroup": pg}

    if operation == "DescribeDBClusterParameterGroups":
        name = bd.get("DBClusterParameterGroupName")
        key  = f"cluster-{name}" if name else None
        if key:
            pg = sm.rds_param_group_get(key)
            return {"DBClusterParameterGroups": [pg]} if pg else {"DBClusterParameterGroups": []}
        all_pgs = [p for p in sm.rds_param_group_list() if p.get("DBClusterParameterGroupName")]
        return {"DBClusterParameterGroups": all_pgs, "Marker": None}

    if operation == "CreateDBSubnetGroup":
        name = bd.get("DBSubnetGroupName", "mock-subnet-group")
        sg   = {
            "DBSubnetGroupName":        name,
            "DBSubnetGroupDescription": bd.get("DBSubnetGroupDescription", "MockMesh subnet group"),
            "VpcId":                    "vpc-mock",
            "SubnetGroupStatus":        "Complete",
            "Subnets": [{"SubnetIdentifier": s, "SubnetStatus": "Active"}
                        for s in bd.get("SubnetIds", ["subnet-mock"])],
            "DBSubnetGroupArn": _sg_arn(name),
        }
        sm.rds_subnet_group_put(name, sg)
        return {"DBSubnetGroup": sg}

    if operation == "DescribeDBSubnetGroups":
        name = bd.get("DBSubnetGroupName")
        if name:
            sg = sm.rds_subnet_group_get(name)
            return {"DBSubnetGroups": [sg]} if sg else {"DBSubnetGroups": []}
        return {"DBSubnetGroups": sm.rds_subnet_group_list(), "Marker": None}

    if operation == "DeleteDBSubnetGroup":
        sm.rds_subnet_group_delete(bd.get("DBSubnetGroupName", ""))
        return {}

    if operation == "CreateOptionGroup":
        name = bd.get("OptionGroupName", "mock-option-group")
        og   = {
            "OptionGroupName":         name,
            "OptionGroupDescription":  bd.get("OptionGroupDescription", "MockMesh option group"),
            "EngineName":              bd.get("EngineName", "mysql"),
            "MajorEngineVersion":      bd.get("MajorEngineVersion", "8.0"),
            "OptionGroupArn":          _og_arn(name),
            "Options":                 [],
        }
        sm.rds_option_group_put(name, og)
        return {"OptionGroup": og}

    if operation == "DescribeOptionGroups":
        name = bd.get("OptionGroupName")
        if name:
            og = sm.rds_option_group_get(name)
            return {"OptionGroupsList": [og]} if og else {"OptionGroupsList": []}
        return {"OptionGroupsList": sm.rds_option_group_list(), "Marker": None}

    if operation == "ModifyOptionGroup":
        name = bd.get("OptionGroupName", "")
        og   = sm.rds_option_group_get(name) or {}
        to_add = bd.get("OptionsToInclude", [])
        to_del = {o.get("OptionName") for o in bd.get("OptionsToRemove", [])}
        existing = [o for o in og.get("Options", []) if o.get("OptionName") not in to_del]
        og["Options"] = existing + to_add
        sm.rds_option_group_put(name, og)
        return {"OptionGroup": og}

    if operation == "DeleteOptionGroup":
        sm.rds_option_group_delete(bd.get("OptionGroupName", ""))
        return {}

    if operation == "CreateDBProxy":
        name = bd.get("DBProxyName", "mock-proxy")
        proxy = {
            "DBProxyName":    name,
            "DBProxyArn":     _proxy_arn(str(uuid.uuid4())),
            "Status":         "available",
            "Endpoint":       _proxy_ep(name),
            "EngineFamily":   bd.get("EngineFamily", "MYSQL"),
            "RoleArn":        bd.get("RoleArn", "arn:aws:iam::123456789012:role/mock-proxy-role"),
            "VpcId":          "vpc-mock",
            "Auth":           bd.get("Auth", []),
        }
        sm.rds_proxy_put(name, proxy)
        return {"DBProxy": proxy}

    if operation == "DescribeDBProxies":
        name = bd.get("DBProxyName")
        if name:
            proxy = sm.rds_proxy_get(name)
            return {"DBProxies": [proxy]} if proxy else {"DBProxies": []}
        return {"DBProxies": sm.rds_proxy_list(), "Marker": None}

    if operation == "ModifyDBProxy":
        name  = bd.get("DBProxyName", "")
        proxy = sm.rds_proxy_get(name) or {}
        proxy["Status"] = "available"
        sm.rds_proxy_put(name, proxy)
        return {"DBProxy": proxy}

    if operation == "DeleteDBProxy":
        name  = bd.get("DBProxyName", "")
        proxy = sm.rds_proxy_get(name) or {"DBProxyName": name}
        proxy["Status"] = "deleting"
        sm.rds_proxy_delete(name)
        return {"DBProxy": proxy}

    if operation == "DescribeDBEngineVersions":
        engine = bd.get("Engine", "mysql")
        return {
            "DBEngineVersions": [
                {
                    "Engine":        engine,
                    "EngineVersion": ver,
                    "DBParameterGroupFamily": f"{engine}{ver.split('.')[0]}.{ver.split('.')[1] if '.' in ver else '0'}",
                    "SupportedFeatureNames": [],
                    "Status": "available",
                }
                for ver in [default_version(engine)]
            ],
            "Marker": None,
        }

    if operation == "DescribeOrderableDBInstanceOptions":
        engine = bd.get("Engine", "mysql")
        cls    = bd.get("DBInstanceClass", "db.t3.micro")
        return {
            "OrderableDBInstanceOptions": [{
                "Engine":          engine,
                "EngineVersion":   default_version(engine),
                "DBInstanceClass": cls,
                "MultiAZCapable":  True,
                "ReadReplicaCapable": True,
                "StorageType":     "gp2",
                "Vpc":             True,
            }],
            "Marker": None,
        }

    if operation == "CreateDBInstanceReadReplica":
        ident  = bd.get("DBInstanceIdentifier", "mock-replica")
        source = sm.rds_instance_get(bd.get("SourceDBInstanceIdentifier", "")) or {}
        engine = source.get("Engine", "mysql")
        inst   = {
            "DBInstanceIdentifier": ident,
            "DBInstanceClass":      bd.get("DBInstanceClass", source.get("DBInstanceClass", "db.t3.micro")),
            "Engine":               engine,
            "EngineVersion":        source.get("EngineVersion", default_version(engine)),
            "DBInstanceStatus":     "available",
            "Endpoint":             _endpoint(ident, engine),
            "ReadReplicaSourceDBInstanceIdentifier": bd.get("SourceDBInstanceIdentifier", ""),
            "StorageType":          source.get("StorageType", "gp2"),
            "DBInstanceArn":        _instance_arn(ident),
        }
        sm.rds_instance_put(ident, inst)
        return {"DBInstance": inst}

    if operation == "PromoteReadReplica":
        ident = bd.get("DBInstanceIdentifier", "")
        inst  = sm.rds_instance_get(ident) or {"DBInstanceIdentifier": ident}
        inst.pop("ReadReplicaSourceDBInstanceIdentifier", None)
        inst["DBInstanceStatus"] = "available"
        sm.rds_instance_put(ident, inst)
        return {"DBInstance": inst}

    if operation == "CreateEventSubscription":
        name = bd.get("SubscriptionName", "mock-sub")
        sub  = {
            "CustSubscriptionId":    name,
            "SnsTopicArn":           bd.get("SnsTopicArn", "arn:aws:sns:us-east-1:123:mock"),
            "Status":                "active",
            "SourceType":            bd.get("SourceType", "db-instance"),
            "EventCategoriesList":   bd.get("EventCategories", []),
            "SourceIdsList":         bd.get("SourceIds", []),
            "Enabled":               bd.get("Enabled", True),
            "EventSubscriptionArn":  _es_arn(name),
        }
        sm.nosql.put("rds_event_subs", name, sub)
        return {"EventSubscription": sub}

    if operation == "DescribeEventSubscriptions":
        name = bd.get("SubscriptionName")
        if name:
            item = sm.nosql.get("rds_event_subs", name)
            return {"EventSubscriptionsList": [item]} if item else {"EventSubscriptionsList": []}
        records = sm.nosql.list("rds_event_subs")
        return {"EventSubscriptionsList": [r["value"] for r in records], "Marker": None}

    if operation == "DeleteEventSubscription":
        name = bd.get("SubscriptionName", "")
        sub  = sm.nosql.get("rds_event_subs", name) or {"CustSubscriptionId": name}
        sub["Status"] = "deleted"
        sm.nosql.delete("rds_event_subs", name)
        return {"EventSubscription": sub}

    return None
