"""Pydantic schemas for GitHub backup configuration."""

import re
from datetime import datetime

from pydantic import BaseModel, Field, model_validator

from backend.app.core.compat import StrEnum


class ScheduleType(StrEnum):
    """Backup schedule types."""

    HOURLY = "hourly"
    DAILY = "daily"
    WEEKLY = "weekly"


class ProviderType(StrEnum):
    """Git hosting provider types."""

    GITHUB = "github"
    GITLAB = "gitlab"
    GITEA = "gitea"
    FORGEJO = "forgejo"


class GitHubBackupConfigCreate(BaseModel):
    """Schema for creating/updating GitHub backup config."""

    repository_url: str = Field(..., min_length=1, max_length=500, description="Git repository URL")
    access_token: str = Field(..., min_length=1, description="Personal Access Token")
    branch: str = Field(default="main", max_length=100, description="Branch to push to")
    provider: ProviderType = Field(default=ProviderType.GITHUB, description="Git hosting provider")

    schedule_enabled: bool = Field(default=False, description="Enable scheduled backups")
    schedule_type: ScheduleType = Field(default=ScheduleType.DAILY, description="Schedule frequency")

    backup_kprofiles: bool = Field(default=True, description="Backup K-profiles")
    backup_cloud_profiles: bool = Field(default=True, description="Backup Bambu Cloud profiles")
    backup_settings: bool = Field(default=False, description="Backup app settings")
    backup_spools: bool = Field(default=False, description="Backup spool inventory")
    backup_archives: bool = Field(default=False, description="Backup print archive history")

    allow_insecure_http: bool = Field(default=False, description="Allow HTTP (non-TLS) repository URLs")
    enabled: bool = Field(default=True, description="Enable backup feature")

    @model_validator(mode="after")
    def validate_repo_url(self) -> "GitHubBackupConfigCreate":
        url = self.repository_url.strip().rstrip("/")
        self.repository_url = url
        https_or_ssh = [
            r"^https://[\w.-]+(:\d+)?/[\w.-]+(\/[\w.-]+)+(?:\.git)?/?$",
            r"^git@[\w.-]+:[\w.-]+(\/[\w.-]+)+(?:\.git)?$",
        ]
        http_pattern = r"^http://[\w.-]+(:\d+)?/[\w.-]+(\/[\w.-]+)+(?:\.git)?/?$"
        if any(re.match(p, url) for p in https_or_ssh):
            return self
        if re.match(http_pattern, url):
            if not self.allow_insecure_http:
                raise ValueError(
                    "This URL uses HTTP instead of HTTPS. "
                    "Enable 'Allow insecure HTTP' if your instance does not use TLS."
                )
            return self
        raise ValueError(
            "Invalid Git repository URL. Expected: https://host/owner/repo, "
            "http://host/owner/repo (with 'Allow insecure HTTP' enabled), or git@host:owner/repo"
        )


class GitHubBackupConfigUpdate(BaseModel):
    """Schema for updating GitHub backup config (all fields optional)."""

    repository_url: str | None = Field(default=None, max_length=500)
    access_token: str | None = Field(default=None)
    branch: str | None = Field(default=None, max_length=100)
    provider: ProviderType | None = None

    schedule_enabled: bool | None = None
    schedule_type: ScheduleType | None = None

    backup_kprofiles: bool | None = None
    backup_cloud_profiles: bool | None = None
    backup_settings: bool | None = None
    backup_spools: bool | None = None
    backup_archives: bool | None = None

    allow_insecure_http: bool | None = None
    enabled: bool | None = None

    @model_validator(mode="after")
    def validate_repo_url(self) -> "GitHubBackupConfigUpdate":
        if self.repository_url is None:
            return self
        url = self.repository_url.strip().rstrip("/")
        self.repository_url = url
        valid_patterns = [
            r"^https?://[\w.-]+(:\d+)?/[\w.-]+(\/[\w.-]+)+(?:\.git)?/?$",
            r"^git@[\w.-]+:[\w.-]+(\/[\w.-]+)+(?:\.git)?$",
        ]
        if not any(re.match(p, url) for p in valid_patterns):
            raise ValueError(
                "Invalid repository URL. Expected: https://host/owner/repo, "
                "http://host/owner/repo, or git@host:owner/repo"
            )
        return self


class GitHubBackupConfigResponse(BaseModel):
    """Schema for GitHub backup config API response."""

    id: int
    repository_url: str
    has_token: bool = Field(description="Whether an access token is configured")
    branch: str
    provider: str
    allow_insecure_http: bool

    schedule_enabled: bool
    schedule_type: str

    backup_kprofiles: bool
    backup_cloud_profiles: bool
    backup_settings: bool
    backup_spools: bool
    backup_archives: bool

    enabled: bool
    last_backup_at: datetime | None
    last_backup_status: str | None
    last_backup_message: str | None
    last_backup_commit_sha: str | None
    next_scheduled_run: datetime | None

    created_at: datetime
    updated_at: datetime

    class Config:
        from_attributes = True


class GitHubBackupLogResponse(BaseModel):
    """Schema for backup log API response."""

    id: int
    config_id: int
    started_at: datetime
    completed_at: datetime | None
    status: str
    trigger: str
    commit_sha: str | None
    files_changed: int
    error_message: str | None

    class Config:
        from_attributes = True


class GitHubBackupStatus(BaseModel):
    """Schema for current backup status."""

    configured: bool = Field(description="Whether backup is configured")
    enabled: bool = Field(description="Whether backup is enabled")
    is_running: bool = Field(description="Whether a backup is currently running")
    progress: str | None = Field(default=None, description="Current backup progress message")
    last_backup_at: datetime | None
    last_backup_status: str | None
    next_scheduled_run: datetime | None


class GitHubTestConnectionResponse(BaseModel):
    """Schema for test connection response."""

    success: bool
    message: str
    repo_name: str | None = None
    permissions: dict | None = None
    # True = confirmed private. False = confirmed public (or non-private such
    # as GitLab "internal"). None = could not be determined (older self-hosted
    # API, non-2xx response). The backup config endpoints refuse anything that
    # isn't an explicit True.
    is_private: bool | None = None


class GitHubBackupTriggerResponse(BaseModel):
    """Schema for manual backup trigger response."""

    success: bool
    message: str
    log_id: int | None = None
    commit_sha: str | None = None
    files_changed: int = 0
