module "default_tags" {
  source             = "git@github.com:theorchard/terraform-default-tags.git//?ref=2.0.0"
  environment        = var.environment
  application_family = var.application_family
  service_name       = var.service_name
  team_name          = "tax-and-payments"
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = module.default_tags.tags
  }
}

terraform {
  backend "s3" {
    bucket  = "orcd-terraform-state"
    key     = "prod/documents/load-from-s3/terraform.tfstate"
    region  = "us-east-1"
    encrypt = "true"
  }
}

module "vpc_info" {
  source = "git@github.com:theorchard/terraform-vpc-info.git?ref=3.1.0"

  environment = var.environment
}

# Local values for better maintainability
locals {
  tax_corrections_paths = [
    "post_tax_corrections/*",
    "post_tax_corrections_vat/*"
  ]

  tax_corrections_resources = [
    for path in local.tax_corrections_paths : "${module.s3_bucket.s3_bucket_arn_output}/${path}"
  ]

  # Ignoring:
  #  - sync_manufactured_upcs
  #  - post_payment_hold
  #  - post_payment_unhold
  #  - post_tax_corrections_vat
  #  - post_tax_corrections
  #  - generate_report_banking_details
  #  - generate_report_non_us_tax_details
  #  - generate_report_tax_form
  #  - purge_banking_details
  #  - register_banking_details
  lifecycle_rules_options_current_version_expiration = [
    {
      prefix  = "reports/"
      enabled = true
      days    = 1
    },
    {
      prefix  = "post_banking_details/"
      enabled = true
      days    = 7
    },
    {
      prefix  = "post_german_tax_details/"
      enabled = true
      days    = 7
    },
    {
      prefix  = "post_norway_tax_details/"
      enabled = true
      days    = 7
    },
    {
      prefix  = "post_spanish_tax_details/"
      enabled = true
      days    = 7
    },
    {
      prefix  = "post_tax_forms/"
      enabled = true
      days    = 7
    },
    {
      prefix  = "post_uk_tax_details/"
      enabled = true
      days    = 7
    }
  ]
}

module "s3_bucket" {
  source = "git@github.com:theorchard/terraform-s3.git//modules/s3_bucket?ref=3.15.6"

  env                 = var.environment
  bucket_name         = var.s3_bucket_name
  acceleration_status = "Enabled"
  application_family  = var.application_family
  apply_server_side_encryption_by_default = {
    sse_algorithm = "AES256"
  }

  # CORS Rules
  bucket_cors_rule = [
    {
      allowed_headers = ["*"]
      allowed_methods = ["GET"]
      allowed_origins = ["*"]
      expose_headers  = ["ETag"]
      max_age_seconds = 3000
    }
  ]

  lifecycle_rules_options_current_version_expiration = local.lifecycle_rules_options_current_version_expiration

  lifecycle_rules_options_noncurrent_version_transition = [
    {
      prefix        = ""
      enabled       = true
      days          = 35
      storage_class = "STANDARD_IA"
    }
  ]

  bucket_policy_overrides = [
    data.aws_iam_policy_document.s3_bucket_policy_tax_corrections_restriction.json
  ]
}

module "secrets" {
  source   = "git@github.com:theorchard/terraform-secrets-manager.git//?ref=1.5.1"
  for_each = toset(var.secrets_manager_secret_names)

  application_family = var.application_family
  environment        = var.environment
  service_name       = var.service_name
  secret_name        = each.value
}

data "aws_iam_policy_document" "s3_input_bucket_policy" {
  statement {
    effect = "Allow"

    actions = [
      "s3:HeadBucket",
      "s3:ListBucket",
      "s3:GetObjectVersion",
      "s3:GetObject",
      "s3:PutObject",
      "s3:PutObjectTagging",
      "s3:BypassGovernanceRetention",
      "s3:GetBucketObjectLockConfiguration",
      "s3:GetObjectLegalHold",
      "s3:GetObjectRetention",
      "s3:PutBucketObjectLockConfiguration",
      "s3:PutObjectLegalHold",
      "s3:PutObjectRetention",
    ]

    resources = [
      "${module.s3_bucket.s3_bucket_arn_output}",
      "${module.s3_bucket.s3_bucket_arn_output}/*",
    ]
  }
}

resource "aws_iam_policy" "s3_input_bucket_policy" {
  name        = "S3-${module.s3_bucket.s3_bucket_name_output}-RW"
  description = "Read, Write access to lambda-documents-load-from-s3 bucket"
  policy      = data.aws_iam_policy_document.s3_input_bucket_policy.json
}

#################################
#
# lambda configuration
#
##################################
module "lambda_documents_load_from_s3" {
  source                   = "git@github.com:theorchard/terraform-lambda.git?ref=5.2.2"
  environment              = var.environment
  lambda_name              = var.service_name
  lambda_description       = "Load data from s3 bucket"
  datadog_enabled          = var.datadog_enabled
  datadog_advanced_enabled = var.datadog_advanced_enabled

  application_family = var.application_family

  # lambda_function_provisioned_concurrent_executions = var.lambda_provisioned_concurrent_executions
  lambda_function_timeout     = "900"
  lambda_function_memory_size = "1024"
  use_container_image         = true

  vpc_enabled             = true
  vpc_id                  = module.vpc_info.vpc_id
  vpc_subnet_ids          = module.vpc_info.default_private_subnet_ids
  zappa_s3_policy_enabled = false


  lambda_function_environment_variables = {
    ENVIRONMENT             = var.environment
    BUCKET_NAME             = module.s3_bucket.s3_bucket_name_output
    SNOWFLAKE_ACCOUNT       = var.snowflake_account
    SNOWFLAKE_ROLE          = var.snowflake_role
    SNOWFLAKE_WAREHOUSE     = var.snowflake_warehouse
    SNOWFLAKE_USER          = var.snowflake_user
    SNOWFLAKE_DATABASE      = var.snowflake_database
    PAYONEER_API_URL        = var.payoneer_api_url
    PAYONEER_AUTH_TOKEN_ARN = data.aws_secretsmanager_secret.payoneer_auth_token.arn
  }

  iam_managed_policy_attachments = [
    aws_iam_policy.s3_input_bucket_policy.arn,
    aws_iam_policy.payoneer_auth_token_read.arn,
    module.lambda_documents_load_from_s3_owsrequest.policy_arn_output
  ]
}

# Payoneer auth token is owned and rotated by the ows-payee service;
# we only read it. `data` lookup rather than `resource` so we do not
# accidentally take ownership (and on destroy we would delete the
# secret the rotator relies on).
data "aws_secretsmanager_secret" "payoneer_auth_token" {
  name = "${var.environment}/${var.payoneer_token_owner_service}/${var.payoneer_token_secret_name}"
}

data "aws_iam_policy_document" "payoneer_auth_token_read" {
  statement {
    effect = "Allow"
    actions = [
      "secretsmanager:GetSecretValue",
      "secretsmanager:DescribeSecret",
    ]
    resources = [data.aws_secretsmanager_secret.payoneer_auth_token.arn]
  }
}

resource "aws_iam_policy" "payoneer_auth_token_read" {
  name        = "${var.environment}-${var.service_name}-payoneer-token-read"
  description = "Read Payoneer auth token for pull_banking_details processor"
  policy      = data.aws_iam_policy_document.payoneer_auth_token_read.json
}

resource "aws_lambda_permission" "allow_load_from_s3_lambda_execution_from_bucket" {
  statement_id  = "AllowExecutionFromS3Bucket"
  action        = "lambda:InvokeFunction"
  function_name = module.lambda_documents_load_from_s3.lambda_name
  principal     = "s3.amazonaws.com"
  source_arn    = module.s3_bucket.s3_bucket_arn_output
}

resource "aws_s3_bucket_notification" "load_from_s3_bucket_notification" {
  bucket = module.s3_bucket.s3_bucket_name_output

  dynamic "lambda_function" {
    for_each = local.lambda_notifications
    content {
      lambda_function_arn = module.lambda_documents_load_from_s3.lambda_arn
      events              = ["s3:ObjectCreated:CompleteMultipartUpload", "s3:ObjectCreated:Put"]
      filter_prefix       = lambda_function.value.prefix
      filter_suffix       = try(lambda_function.value.suffix, ".csv")
    }
  }

  depends_on = [aws_lambda_permission.allow_load_from_s3_lambda_execution_from_bucket]
}

module "lambda_documents_load_from_s3_owsrequest" {
  source = "git@github.com:theorchard/terraform-owsrequest.git?ref=1.1.0"

  environment_name = var.environment
  service_name     = var.service_name
}

# create datadog dashboards.
module "datadog_lambda_documents_load_from_s3" {
  source = "git@github.com:theorchard/terraform-datadog.git//modules/lambda?ref=6.16.2"

  environment                       = var.environment
  service_name                      = var.service_name
  lambda_invocation_monitor_enabled = false

  notification_endpoints            = "@slack-The_Orchard-documents-public"
  escalation_notification_endpoints = "@slack-The_Orchard-documents-public"

  lambda_error_ok_number                = 0
  lambda_error_warning_recovery_number  = null
  lambda_error_critical_recovery_number = null
  lambda_error_warning_number           = null
  lambda_error_critical_number          = 1
}

module "lambda_documents_load_from_s3_sentry_project" {
  source = "git@github.com:theorchard/terraform-sentry.git?ref=4.1.2"

  environment        = var.environment
  platform           = "python"
  service_name       = var.service_name
  teams              = [var.environment]
  application_family = var.application_family
}

data "aws_iam_policy_document" "load_from_s3_invoke_policy_document" {
  statement {
    effect = "Allow"
    actions = [
      "lambda:InvokeFunction",
      "lambda:GetFunction",
      "lambda:GetFunctionConfiguration",
      "lambda:GetPolicy",
      "lambda:GetAlias",
      "lambda:ListVersionsByFunction",
      "lambda:ListAliases",
      "lambda:GetFunctionEventInvokeConfig"
    ]
    resources = [
      module.lambda_documents_load_from_s3.lambda_arn
    ]
  }
}

resource "aws_iam_policy" "load_from_s3_invoke_policy" {
  name   = "${var.environment}-load-from-s3-invoke-policy"
  policy = data.aws_iam_policy_document.load_from_s3_invoke_policy_document.json

  tags = {
    application_family = var.application_family
    environment        = var.environment
    service_name       = var.service_name
    terraformed        = "true"
  }
}

module "m2m_secrets" {
  source = "git@github.com:theorchard/terraform-secrets-manager.git//modules/auth0m2m?ref=1.6.1"

  environment        = var.environment
  service_name       = var.service_name
  application_family = var.application_family
}

# Sean Golden user is managed in prod/iam/users/orchard/password/variables.tf
data "aws_iam_user" "sean_golden" {
  user_name = "sgolden"
}

data "aws_iam_policy_document" "finance_team_tax_corrections_policy" {
  statement {
    effect = "Allow"
    actions = [
      "s3:ListBucket"
    ]
    resources = [
      module.s3_bucket.s3_bucket_arn_output
    ]
    condition {
      test     = "StringLike"
      variable = "s3:prefix"
      values   = local.tax_corrections_paths
    }
  }

  statement {
    effect = "Allow"
    actions = [
      "s3:GetObject",
      "s3:GetObjectVersion",
      "s3:PutObject",
      "s3:PutObjectAcl",
      "s3:DeleteObject",
      "s3:DeleteObjectVersion"
    ]
    resources = local.tax_corrections_resources
  }
}

resource "aws_iam_policy" "tax_corrections_s3_access_policy" {
  name        = "${var.environment}-tax-corrections-s3-access"
  description = "Allows access to tax corrections folders in S3"
  policy      = data.aws_iam_policy_document.finance_team_tax_corrections_policy.json

  tags = {
    application_family = var.application_family
    environment        = var.environment
    service_name       = var.service_name
    terraformed        = "true"
  }
}

# IAM Group for Tax Corrections Access (Best Practice for Scalability)
resource "aws_iam_group" "tax_corrections_access" {
  name = "${var.environment}-tax-corrections-s3-access"
  path = "/"
}

resource "aws_iam_group_policy_attachment" "tax_corrections_group_policy" {
  group      = aws_iam_group.tax_corrections_access.name
  policy_arn = aws_iam_policy.tax_corrections_s3_access_policy.arn
}

# Add Sean Golden to the tax corrections group
resource "aws_iam_user_group_membership" "finance_team_tax_corrections_group" {
  user = data.aws_iam_user.sean_golden.user_name
  groups = [
    aws_iam_group.tax_corrections_access.name
  ]
}

# S3 Bucket Policy for Tax Corrections Access Control
# This policy implements a dual-layer security model:
# 1. IAM policies define what users/roles CAN do
# 2. S3 bucket policy defines additional restrictions and explicit permissions
# This approach provides defense in depth and ensures bucket-level access control
data "aws_iam_policy_document" "s3_bucket_policy_tax_corrections_restriction" {

  # Allow Finance Team full access to tax corrections folders
  statement {
    sid    = "AllowFinanceTeamTaxCorrectionsListBucket"
    effect = "Allow"

    principals {
      type        = "AWS"
      identifiers = [data.aws_iam_user.sean_golden.arn]
    }

    actions = [
      "s3:ListBucket"
    ]

    resources = [
      module.s3_bucket.s3_bucket_arn_output
    ]

    # Restrict ListBucket to only tax corrections prefixes
    condition {
      test     = "StringLike"
      variable = "s3:prefix"
      values   = local.tax_corrections_paths
    }
  }

  # Allow Finance Team object-level access to tax corrections folders
  statement {
    sid    = "AllowFinanceTeamTaxCorrectionsObjectAccess"
    effect = "Allow"

    principals {
      type        = "AWS"
      identifiers = [data.aws_iam_user.sean_golden.arn]
    }

    actions = [
      "s3:GetObject",
      "s3:GetObjectVersion",
      "s3:PutObject",
      "s3:PutObjectAcl",
      "s3:DeleteObject",
      "s3:DeleteObjectVersion"
    ]

    resources = local.tax_corrections_resources
  }

  # Allow Lambda execution role access to tax corrections folders
  statement {
    sid    = "AllowLambdaExecutionRoleTaxCorrections"
    effect = "Allow"

    principals {
      type        = "AWS"
      identifiers = [module.lambda_documents_load_from_s3.lambda_role_arn]
    }

    actions = [
      "s3:GetObject",
      "s3:GetObjectVersion",
      "s3:PutObject",
      "s3:PutObjectAcl",
      "s3:DeleteObject",
      "s3:DeleteObjectVersion"
    ]

    resources = local.tax_corrections_resources
  }

  # Explicit deny for all other IAM users trying to access tax corrections folders
  # This provides an additional security layer beyond IAM policies
  statement {
    sid    = "DenyOtherIAMUsersTaxCorrectionsAccess"
    effect = "Deny"

    principals {
      type        = "AWS"
      identifiers = ["*"]
    }

    actions = [
      "s3:PutObject",
      "s3:PutObjectAcl",
      "s3:DeleteObject",
      "s3:DeleteObjectVersion"
    ]

    resources = local.tax_corrections_resources

    # Only apply to IAM users (not service roles, root, or federated users)
    condition {
      test     = "StringLike"
      variable = "aws:arn"
      values   = ["arn:aws:iam::*:user/*"]
    }

    # Exclude Finance Team from this deny
    condition {
      test     = "StringNotEquals"
      variable = "aws:username"
      values   = ["sgolden"]
    }
  }
}
