"""Pre-defined access policies and setup guides for cloud providers.

Each policy specifies the **minimum** permissions required (principle of least
privilege) together with human-readable setup instructions.
"""
from __future__ import annotations

from mesh.vault.models import AccessPolicy, CloudProvider, ServiceRequirement

POLICIES: list[AccessPolicy] = [
    # ── AWS ───────────────────────────────────────────────────────

    AccessPolicy(
        provider=CloudProvider.AWS,
        service=ServiceRequirement.MODEL_STORAGE,
        required_fields=["access_key_id", "secret_access_key", "region"],
        optional_fields=["session_token", "endpoint_url"],
        required_permissions=["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
        description="S3 access for JEPA model storage and sync",
        setup_guide="""\
## AWS S3 -- Model Storage

### IAM Policy (Least Privilege)
```json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
    "Resource": [
      "arn:aws:s3:::your-mesh-bucket",
      "arn:aws:s3:::your-mesh-bucket/*"
    ]
  }]
}
```

### Setup Steps
1. Create IAM user `mesh-model-storage` with programmatic access
2. Attach the policy above
3. Use `mesh-vault add --provider aws --service model_storage`
4. Enter access_key_id and secret_access_key when prompted
""",
    ),
    AccessPolicy(
        provider=CloudProvider.AWS,
        service=ServiceRequirement.JOB_EXECUTION,
        required_fields=["access_key_id", "secret_access_key", "region"],
        optional_fields=["session_token", "role_arn"],
        required_permissions=[
            "ec2:RunInstances", "ec2:TerminateInstances", "ec2:DescribeInstances",
            "ecs:RunTask", "ecs:StopTask", "ecs:DescribeTasks",
            "iam:PassRole",
        ],
        description="EC2/ECS access for running mesh jobs on AWS",
        setup_guide="""\
## AWS EC2/ECS -- Job Execution

### IAM Policy (Least Privilege)
```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:RunInstances", "ec2:TerminateInstances",
        "ec2:DescribeInstances", "ec2:DescribeInstanceStatus"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {"aws:RequestedRegion": "us-east-1"}
      }
    },
    {
      "Effect": "Allow",
      "Action": ["ecs:RunTask", "ecs:StopTask", "ecs:DescribeTasks"],
      "Resource": "arn:aws:ecs:*:*:task/mesh-*"
    },
    {
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::*:role/mesh-execution-role"
    }
  ]
}
```

### Setup Steps
1. Create IAM user `mesh-job-executor` with programmatic access
2. Attach the policy above (restrict region as needed)
3. Use `mesh-vault add --provider aws --service job_execution`
""",
    ),
    AccessPolicy(
        provider=CloudProvider.AWS,
        service=ServiceRequirement.ATLAS_BACKUP,
        required_fields=["access_key_id", "secret_access_key", "region", "bucket_name"],
        optional_fields=["prefix", "endpoint_url"],
        required_permissions=["s3:PutObject", "s3:GetObject", "s3:ListBucket", "s3:DeleteObject"],
        description="S3 access for atlas database backups",
        setup_guide="""\
## AWS S3 -- Atlas Backup

### IAM Policy (Least Privilege)
```json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:PutObject", "s3:GetObject", "s3:ListBucket", "s3:DeleteObject"],
    "Resource": [
      "arn:aws:s3:::your-mesh-backup-bucket",
      "arn:aws:s3:::your-mesh-backup-bucket/atlas/*"
    ]
  }]
}
```

### Setup Steps
1. Create an S3 bucket with versioning enabled
2. Create IAM user `mesh-atlas-backup` with the policy above
3. Use `mesh-vault add --provider aws --service atlas_backup`
""",
    ),
    AccessPolicy(
        provider=CloudProvider.AWS,
        service=ServiceRequirement.NOTIFICATION,
        required_fields=["access_key_id", "secret_access_key", "region"],
        optional_fields=["topic_arn", "sender_email"],
        required_permissions=["sns:Publish", "ses:SendEmail"],
        description="SNS/SES access for mesh alert notifications",
        setup_guide="""\
## AWS SNS/SES -- Notifications

### IAM Policy (Least Privilege)
```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sns:Publish",
      "Resource": "arn:aws:sns:*:*:mesh-alerts"
    },
    {
      "Effect": "Allow",
      "Action": "ses:SendEmail",
      "Resource": "*",
      "Condition": {
        "StringEquals": {"ses:FromAddress": "mesh-alerts@yourdomain.com"}
      }
    }
  ]
}
```

### Setup Steps
1. Create SNS topic `mesh-alerts` and subscribe your email/Slack
2. Verify sender email in SES
3. Create IAM user `mesh-notifications` with the policy above
4. Use `mesh-vault add --provider aws --service notification`
""",
    ),

    # ── GCP ───────────────────────────────────────────────────────

    AccessPolicy(
        provider=CloudProvider.GCP,
        service=ServiceRequirement.MODEL_STORAGE,
        required_fields=["service_account_json"],
        optional_fields=["project_id", "bucket_name"],
        required_permissions=["storage.objects.get", "storage.objects.create", "storage.objects.list"],
        description="GCS access for JEPA model storage and sync",
        setup_guide="""\
## GCP Cloud Storage -- Model Storage

### IAM Role (Least Privilege)
Create a custom role with:
- `storage.objects.get`
- `storage.objects.create`
- `storage.objects.list`

Or use the predefined `roles/storage.objectAdmin` scoped to a single bucket.

### Setup Steps
1. Create a service account `mesh-model-storage@project.iam.gserviceaccount.com`
2. Grant `roles/storage.objectAdmin` on the target bucket
3. Download the JSON key file
4. Use `mesh-vault add --provider gcp --service model_storage`
5. Paste the full JSON key content when prompted for `service_account_json`
""",
    ),
    AccessPolicy(
        provider=CloudProvider.GCP,
        service=ServiceRequirement.JOB_EXECUTION,
        required_fields=["service_account_json"],
        optional_fields=["project_id", "zone", "cluster_name"],
        required_permissions=[
            "compute.instances.create", "compute.instances.delete",
            "compute.instances.get", "container.pods.create",
        ],
        description="Compute Engine/GKE access for running mesh jobs",
        setup_guide="""\
## GCP Compute/GKE -- Job Execution

### IAM Roles (Least Privilege)
- `roles/compute.instanceAdmin.v1` (scoped to mesh instances)
- `roles/container.developer` (if using GKE)

### Setup Steps
1. Create service account `mesh-job-executor@project.iam.gserviceaccount.com`
2. Grant the roles above
3. Download the JSON key file
4. Use `mesh-vault add --provider gcp --service job_execution`
""",
    ),
    AccessPolicy(
        provider=CloudProvider.GCP,
        service=ServiceRequirement.TELEMETRY,
        required_fields=["service_account_json"],
        optional_fields=["project_id"],
        required_permissions=["monitoring.timeSeries.create", "monitoring.metricDescriptors.create"],
        description="Cloud Monitoring access for mesh telemetry export",
        setup_guide="""\
## GCP Cloud Monitoring -- Telemetry

### IAM Role (Least Privilege)
- `roles/monitoring.metricWriter`

### Setup Steps
1. Create service account `mesh-telemetry@project.iam.gserviceaccount.com`
2. Grant `roles/monitoring.metricWriter`
3. Download the JSON key file
4. Use `mesh-vault add --provider gcp --service telemetry`
""",
    ),

    # ── Azure ─────────────────────────────────────────────────────

    AccessPolicy(
        provider=CloudProvider.AZURE,
        service=ServiceRequirement.MODEL_STORAGE,
        required_fields=["storage_account_name", "storage_account_key"],
        optional_fields=["container_name", "connection_string"],
        required_permissions=["Storage Blob Data Contributor"],
        description="Azure Blob Storage for JEPA model storage",
        setup_guide="""\
## Azure Blob Storage -- Model Storage

### RBAC Role (Least Privilege)
Assign `Storage Blob Data Contributor` scoped to the storage account or container.

### Setup Steps
1. Create a storage account for mesh data
2. Create a container for models
3. Generate an access key or create a service principal
4. Use `mesh-vault add --provider azure --service model_storage`
""",
    ),
    AccessPolicy(
        provider=CloudProvider.AZURE,
        service=ServiceRequirement.JOB_EXECUTION,
        required_fields=["tenant_id", "client_id", "client_secret", "subscription_id"],
        optional_fields=["resource_group"],
        required_permissions=["Contributor (scoped to resource group)"],
        description="ACI/AKS access for running mesh jobs on Azure",
        setup_guide="""\
## Azure ACI/AKS -- Job Execution

### RBAC Role (Least Privilege)
Assign `Contributor` scoped to a dedicated resource group.

### Setup Steps
1. Create resource group `mesh-jobs`
2. Register an App in Azure AD
3. Create a client secret
4. Assign `Contributor` on the resource group
5. Use `mesh-vault add --provider azure --service job_execution`
""",
    ),

    # ── Docker ────────────────────────────────────────────────────

    AccessPolicy(
        provider=CloudProvider.DOCKER,
        service=ServiceRequirement.IMAGE_REGISTRY,
        required_fields=["username", "password"],
        optional_fields=["registry_url", "email"],
        required_permissions=["pull", "push (if building images)"],
        description="Docker Hub or private registry credentials for image pulls",
        setup_guide="""\
## Docker Registry -- Image Registry

### Access Tokens (Least Privilege)
Use a Docker Hub access token with **read-only** scope if you only need pulls.
For private registries, create a service account with pull access.

### Setup Steps
1. Go to Docker Hub > Account Settings > Security > Access Tokens
2. Create a token with `Read-only` scope (or `Read/Write` if pushing)
3. Use `mesh-vault add --provider docker --service image_registry`
4. Enter your Docker Hub username and the access token as password

For private registries, set `registry_url` to your registry endpoint.
""",
    ),

    # ── GitHub ────────────────────────────────────────────────────

    AccessPolicy(
        provider=CloudProvider.GITHUB,
        service=ServiceRequirement.SOURCE_CONTROL,
        required_fields=["personal_access_token"],
        optional_fields=["username", "api_url"],
        required_permissions=["repo (read/write)", "read:org (if org repos)"],
        description="GitHub PAT for source control operations",
        setup_guide="""\
## GitHub -- Source Control

### Personal Access Token (Least Privilege)
Use a **fine-grained** PAT scoped to specific repositories:
- `Contents: Read and write`
- `Metadata: Read-only`

### Setup Steps
1. Go to GitHub > Settings > Developer Settings > Fine-grained tokens
2. Create a token scoped to your mesh repositories
3. Grant `Contents: Read and write` permission
4. Use `mesh-vault add --provider github --service source_control`
""",
    ),
    AccessPolicy(
        provider=CloudProvider.GITHUB,
        service=ServiceRequirement.UPDATE_SERVER,
        required_fields=["personal_access_token"],
        optional_fields=["api_url", "repo_owner", "repo_name"],
        required_permissions=["contents:read (for release assets)"],
        description="GitHub Releases API access for auto-update downloads",
        setup_guide="""\
## GitHub Releases -- Update Server

### Personal Access Token (Least Privilege)
Use a **fine-grained** PAT with read-only access:
- `Contents: Read-only`

### Setup Steps
1. Create a fine-grained PAT scoped to the mesh-optimizer repository
2. Grant `Contents: Read-only` permission
3. Use `mesh-vault add --provider github --service update_server`
""",
    ),
]


def get_policy(
    provider: CloudProvider,
    service: ServiceRequirement,
) -> AccessPolicy | None:
    """Look up the policy for a given provider+service combination."""
    for p in POLICIES:
        if p.provider == provider and p.service == service:
            return p
    return None


def get_policies_for_provider(provider: CloudProvider) -> list[AccessPolicy]:
    """Return all policies for a given cloud provider."""
    return [p for p in POLICIES if p.provider == provider]


def get_policies_for_service(service: ServiceRequirement) -> list[AccessPolicy]:
    """Return all policies for a given service requirement."""
    return [p for p in POLICIES if p.service == service]
