# Phase 1: Initial Deployment - 5 US Commercial Azure Regions
# DeFi Oracle Meta Mainnet (ChainID 138)
# 
# Architecture:
# - West Europe: Admin/control-plane only (no workload)
# - 5 US Commercial Azure regions: VMs with Standard_D8plsv6
# - Ubuntu 22.04 Gen 2, Docker Engine, NVM, Node 22 LTS, JDK 17
# - Nginx Proxy Server to connect Cloudflare to the 5 regions
# - SSH access for management
#
# NOTE: Phase 3 (36-region global AKS deployment) is archived in phases/phase3/

terraform {
  required_version = ">= 1.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }

  # Backend configuration is in backend.tf (separate file)
}

provider "azurerm" {
  features {
    resource_group {
      prevent_deletion_if_contains_resources = var.environment == "prod" ? true : false
    }
    key_vault {
      purge_soft_delete_on_destroy    = var.environment == "prod" ? false : true
      recover_soft_deleted_key_vaults = true
    }
  }
}

# Local values for naming and configuration
locals {
  cloud_provider = "az"

  env_codes = {
    prod    = "p"
    dev     = "d"
    test    = "t"
    staging = "s"
  }

  env_code = local.env_codes[var.environment]

  # 5 US Commercial Azure regions for Phase 1
  us_regions = [
    "eastus",
    "westus",
    "centralus",
    "eastus2",
    "westus2"
  ]

  # Region code mapping for US regions
  us_region_codes = {
    eastus    = "eus"
    westus    = "wus"
    centralus = "cus"
    eastus2   = "eus2"
    westus2   = "wus2"
  }

  # Region-specific address spaces (to avoid conflicts if VPN/ExpressRoute connects regions)
  region_address_spaces = {
    eastus     = "10.1.0.0/16"
    westus     = "10.2.0.0/16"
    centralus  = "10.3.0.0/16"
    eastus2    = "10.4.0.0/16"
    westus2    = "10.5.0.0/16"
    westeurope = "10.10.0.0/16"
  }

  # Region-specific subnet prefixes
  region_subnet_prefixes = {
    eastus     = "10.1.1.0/24"
    westus     = "10.2.1.0/24"
    centralus  = "10.3.1.0/24"
    eastus2    = "10.4.1.0/24"
    westus2    = "10.5.1.0/24"
    westeurope = "10.10.1.0/24"
  }

      # Phase 1 configuration for each US region
      # NOTE: Standard_D8pls_v6 is ARM64-only, but we're using x64 Ubuntu image
      # Using Standard_D8s_v6 (x64) for compatibility with x64 Ubuntu 22.04 Gen 2 image
      # East US has capacity restrictions for D8s_v6, using D8s_v5 as fallback
      phase1_config = {
        for region in local.us_regions : region => {
          location    = region
          region_code = lookup(local.us_region_codes, region, substr(region, 0, 3))
          # Use Standard_D8s_v4 for eastus (quota restrictions), Standard_D8s_v6 for others
          vm_size     = region == "eastus" ? "Standard_D8s_v4" : "Standard_D8s_v6"
          vm_count    = 1                  # 1 VM per region for Phase 1
        }
      }

  # Common tags
  common_tags = merge(var.tags, {
    Environment      = var.environment
    CostCenter       = "Blockchain"
    Owner            = "DevOps Team"
    NamingConvention = "az-env-region-resource-instance"
    DeploymentPhase  = "phase1"
    Project          = "DeFi Oracle Meta Mainnet"
    ChainID          = "138"
  })

  # Naming for admin resources (West Europe)
  name_prefix         = "${local.cloud_provider}-${local.env_code}-wst"
  kv_secrets          = "${local.name_prefix}-kv-secrets-001"
  resource_group_name = var.resource_group_name != "" ? var.resource_group_name : "${local.cloud_provider}-${local.env_code}-wst-rg-comp-001"
}

# Resource Groups for each US region
resource "azurerm_resource_group" "phase1_us_regions" {
  for_each = local.phase1_config

  name     = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-rg-comp-001"
  location = each.value.location

  tags = merge(local.common_tags, {
    Region     = each.value.location
    Deployment = "phase1-us-regions"
  })
}

# Storage accounts for boot diagnostics (one per region)
# Improved naming to reduce collision risk: add region index for additional uniqueness
resource "azurerm_storage_account" "boot_diagnostics" {
  for_each = local.phase1_config

  # Improved naming: add region index to reduce collision risk
  name                     = substr("${local.cloud_provider}${local.env_code}${each.value.region_code}diag${substr(md5("${each.value.location}-boot-${each.key}"), 0, 6)}", 0, 24)
  resource_group_name      = azurerm_resource_group.phase1_us_regions[each.key].name
  location                 = each.value.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  min_tls_version          = "TLS1_2"

  tags = merge(local.common_tags, {
    Region     = each.value.location
    Purpose    = "boot-diagnostics"
    Deployment = "phase1-us-regions"
  })
}

# Storage accounts for backups (one per region)
module "storage_phase1" {
  for_each = local.phase1_config
  source   = "../../modules/storage"

  resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
  location            = each.value.location
  cluster_name        = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
  environment         = var.environment
  tags = merge(local.common_tags, {
    Region     = each.value.location
    Deployment = "phase1-us-regions"
  })
}

# Log Analytics Workspace (one per region for monitoring)
module "monitoring_phase1" {
  for_each = local.phase1_config
  source   = "../../modules/monitoring"

  resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
  location            = each.value.location
  cluster_name        = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
  environment         = var.environment
  tags = merge(local.common_tags, {
    Region     = each.value.location
    Deployment = "phase1-us-regions"
  })
}

# Recovery Services Vault (one per region for backups)
module "backup_phase1" {
  for_each = local.phase1_config
  source   = "../../modules/backup"

  resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
  location            = each.value.location
  cluster_name        = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
  environment         = var.environment
  tags = merge(local.common_tags, {
    Region     = each.value.location
    Deployment = "phase1-us-regions"
  })
}

# Networking for each US region
module "networking_phase1" {
  for_each = local.phase1_config
  source   = "../../modules/networking-vm"

  resource_group_name = azurerm_resource_group.phase1_us_regions[each.key].name
  location            = each.value.location
  cluster_name        = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
  environment         = var.environment
  vnet_address_space  = lookup(local.region_address_spaces, each.value.location, "10.0.0.0/16")
  subnet_address_prefix = lookup(local.region_subnet_prefixes, each.value.location, "10.0.1.0/24")
  allowed_ssh_ips      = var.allowed_ssh_ips
  allowed_rpc_ips      = var.allowed_rpc_ips
  allowed_p2p_ips      = var.allowed_p2p_ips
  allowed_metrics_ips  = var.allowed_metrics_ips
  tags = merge(local.common_tags, {
    Region     = each.value.location
    Deployment = "phase1-us-regions"
  })
}

# VMs for each US region
module "vm_phase1" {
  for_each = local.phase1_config
  source   = "../../modules/vm-deployment"

  resource_group_name       = azurerm_resource_group.phase1_us_regions[each.key].name
  location                  = each.value.location
  cluster_name              = "${local.cloud_provider}-${local.env_code}-${each.value.region_code}-vm"
  node_type                 = "besu-node"
  node_count                = each.value.vm_count
  vm_size                   = each.value.vm_size
  subnet_id                 = module.networking_phase1[each.key].vm_subnet_id
  network_security_group_id = module.networking_phase1[each.key].vm_nsg_id
  admin_username            = var.vm_admin_username
  ssh_public_key            = var.ssh_public_key
  storage_account_name      = azurerm_storage_account.boot_diagnostics[each.key].name # Boot diagnostics storage
  key_vault_id              = module.keyvault.key_vault_id
  genesis_file_path         = ""   # Will be configured later
  use_phase1_cloud_init     = true # Use Phase 1 cloud-init with NVM, Node 22, JDK 17
  tags = merge(local.common_tags, {
    Region      = each.value.location
    Deployment  = "phase1-us-regions"
    Environment = var.environment
  })
}

# Networking for Nginx Proxy (West Europe admin region)
module "networking_admin" {
  source = "../../modules/networking-vm"

  resource_group_name = azurerm_resource_group.main[0].name
  location            = var.location # West Europe
  cluster_name        = "${local.cloud_provider}-${local.env_code}-wst-proxy"
  environment         = var.environment
  vnet_address_space  = lookup(local.region_address_spaces, var.location, "10.10.0.0/16")
  subnet_address_prefix = lookup(local.region_subnet_prefixes, var.location, "10.10.1.0/24")
  allowed_ssh_ips      = var.allowed_ssh_ips
  allowed_rpc_ips      = var.allowed_rpc_ips
  allowed_p2p_ips      = var.allowed_p2p_ips
  allowed_metrics_ips  = var.allowed_metrics_ips
  subnet_nsg_enabled   = false  # Nginx proxy uses NIC-level NSG, not subnet-level
  enable_besu_rules    = false  # Nginx proxy doesn't need Besu-specific rules
  tags = merge(local.common_tags, {
    Region     = var.location
    Deployment = "admin-control-plane"
  })
}

# Nginx Proxy Server (deployed in West Europe admin region)
module "nginx_proxy" {
  source = "../../modules/nginx-proxy"

  resource_group_name = azurerm_resource_group.main[0].name
  location            = var.location # West Europe
  cluster_name        = "${local.cloud_provider}-${local.env_code}-wst-proxy"
  subnet_id           = module.networking_admin.vm_subnet_id
  backend_vms = {
    for k, v in module.vm_phase1 : k => {
      region      = v.location
      private_ips = v.private_ip_addresses # Backend VMs use private IPs only
      public_ips  = []                     # No public IPs for backend VMs (Cloudflare Tunnel handles connectivity)
    }
  }
  admin_username = var.vm_admin_username
  ssh_public_key = var.ssh_public_key
  environment    = var.environment
  tags           = local.common_tags

  depends_on = [
    module.vm_phase1,
    module.networking_phase1,
    module.networking_admin
  ]
}

# West Europe admin resource group (for Key Vault and Nginx Proxy)
resource "azurerm_resource_group" "main" {
  count    = var.use_well_architected ? 0 : 1
  name     = local.resource_group_name
  location = var.location # West Europe

  tags = merge(local.common_tags, {
    Region     = var.location
    Deployment = "admin-control-plane"
  })
}

# Key Vault (shared, in West Europe admin region)
module "keyvault" {
  source = "../../modules/secrets"

  resource_group_name = var.use_well_architected ? var.security_resource_group_name : azurerm_resource_group.main[0].name
  location            = var.location
  key_vault_name      = var.key_vault_name != "" ? var.key_vault_name : local.kv_secrets
  environment         = var.environment
  allowed_ips          = var.key_vault_allowed_ips
  allowed_subnets     = var.key_vault_allowed_subnets
  tags                = local.common_tags
}

# Data source for current client config (for Key Vault access policies)
data "azurerm_client_config" "current" {}

# Key Vault access policies for VM Managed Identities
# NOTE: VMs need access to Key Vault to retrieve secrets using Managed Identity
# Use static keys from phase1_config to avoid for_each dependency issues
resource "azurerm_key_vault_access_policy" "vm_phase1" {
  for_each = local.phase1_config

  key_vault_id = module.keyvault.key_vault_id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  # Use the principal_id from the VM module output (will be known after VM creation)
  object_id    = module.vm_phase1[each.key].principal_ids[0]

  secret_permissions = [
    "Get",
    "List"
  ]

  key_permissions = [
    "Get",
    "List"
  ]

  certificate_permissions = [
    "Get",
    "List"
  ]

  depends_on = [
    module.vm_phase1,
    module.keyvault
  ]
}

# Key Vault access policy for Nginx Proxy Managed Identity
resource "azurerm_key_vault_access_policy" "nginx_proxy" {
  key_vault_id = module.keyvault.key_vault_id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = module.nginx_proxy.principal_id

  secret_permissions = [
    "Get",
    "List"
  ]

  key_permissions = [
    "Get",
    "List"
  ]

  certificate_permissions = [
    "Get",
    "List"
  ]

  depends_on = [
    module.nginx_proxy,
    module.keyvault
  ]
}

# Outputs
output "phase1_us_regions" {
  value = {
    for k, v in module.vm_phase1 : k => {
      region                   = local.phase1_config[k].location
      resource_group           = azurerm_resource_group.phase1_us_regions[k].name
      vm_names                 = v.vm_names
      public_ips               = v.public_ip_addresses
      private_ips              = v.private_ip_addresses
      boot_diagnostics_storage = azurerm_storage_account.boot_diagnostics[k].name
      backup_storage           = module.storage_phase1[k].backup_storage_account_name
    }
  }
  description = "Phase 1 US region deployment information"
}

output "nginx_proxy" {
  value = {
    fqdn          = module.nginx_proxy.fqdn
    public_ip     = module.nginx_proxy.public_ip
    private_ip    = module.nginx_proxy.private_ip
    backend_count = length(local.us_regions)
    note          = "Backend VMs use private IPs only. Deploy VPN/ExpressRoute or Cloudflare Tunnel on backend VMs for connectivity."
  }
  description = "Nginx Proxy Server information (Cloudflare Tunnel integration point)"
}

output "key_vault_name" {
  value       = module.keyvault.key_vault_name
  description = "Name of the Key Vault (West Europe admin region)"
}

output "monitoring" {
  value = {
    for k, v in module.monitoring_phase1 : k => {
      log_analytics_workspace_id   = v.log_analytics_workspace_id
      log_analytics_workspace_name = v.log_analytics_workspace_name
    }
  }
  description = "Log Analytics Workspace information for monitoring"
}

output "backups" {
  value = {
    for k, v in module.backup_phase1 : k => {
      recovery_services_vault_id   = v.recovery_services_vault_id
      recovery_services_vault_name = v.recovery_services_vault_name
      backup_policy_id            = v.backup_policy_id
    }
  }
  description = "Recovery Services Vault information for backups"
}

# Comprehensive outputs with SSH strings and resource IDs
output "ssh_connection_strings" {
  value = {
    for k, v in module.vm_phase1 : k => {
      for idx, vm_name in v.vm_names : vm_name => "ssh ${var.vm_admin_username}@${length(v.public_ip_addresses) > idx ? v.public_ip_addresses[idx] : v.private_ip_addresses[idx]}"
    }
  }
  description = "SSH connection strings for VMs (uses public IP if available, otherwise private IP)"
}

output "nginx_proxy_ssh" {
  value       = "ssh ${var.vm_admin_username}@${module.nginx_proxy.public_ip}"
  description = "SSH connection string for Nginx Proxy"
}

output "resource_ids" {
  value = {
    resource_groups = {
      for k, v in azurerm_resource_group.phase1_us_regions : k => v.id
    }
    vms = {
      for k, v in module.vm_phase1 : k => {
        for idx, vm_id in v.vm_ids : v.vm_names[idx] => vm_id
      }
    }
    key_vault = {
      id  = module.keyvault.key_vault_id
      uri = module.keyvault.key_vault_uri
    }
  }
  description = "Resource IDs for all deployed resources"
}

output "storage_accounts" {
  value = {
    boot_diagnostics = {
      for k, v in azurerm_storage_account.boot_diagnostics : k => {
        name     = v.name
        location = v.location
      }
    }
    backups = {
      for k, v in module.storage_phase1 : k => {
        backup_storage = v.backup_storage_account_name
        shared_storage = v.shared_storage_account_name
      }
    }
  }
  description = "Storage account information for boot diagnostics and backups"
}

