package dto

import (
	"encoding/json"

	"github.com/go-playground/validator/v10"
)

// RawJSON is a swagger-friendly wrapper for json.RawMessage.
type RawJSON = json.RawMessage

var Validate = validator.New()

func init() {
	_ = Validate.RegisterValidation("awsarn", func(fl validator.FieldLevel) bool {
		v := fl.Field().String()
		return len(v) > 10 && len(v) < 2048 && len(v) >= 4 && v[:4] == "arn:"
	})
}

/*** Shapes for secrets ***/

type AWSCredential struct {
	AccessKeyID     string `json:"access_key_id" validate:"required,alphanum,len=20"`
	SecretAccessKey string `json:"secret_access_key" validate:"required"`
	Region          string `json:"region" validate:"omitempty"`
}

type BasicAuth struct {
	Username string `json:"username" validate:"required"`
	Password string `json:"password" validate:"required"`
}

type APIToken struct {
	Token string `json:"token" validate:"required"`
}

type OAuth2Credential struct {
	ClientID     string `json:"client_id" validate:"required"`
	ClientSecret string `json:"client_secret" validate:"required"`
	RefreshToken string `json:"refresh_token" validate:"required"`
}

/*** Shapes for scopes ***/

type AWSProviderScope struct{}

type AWSServiceScope struct {
	Service string `json:"service" validate:"required,oneof=route53 s3 ec2 iam rds dynamodb"`
}

type AWSResourceScope struct {
	ARN string `json:"arn" validate:"required,awsarn"`
}

/*** Registries ***/

type ProviderDef struct {
	New      func() any
	Validate func(any) error
}

type ScopeDef struct {
	New         func() any
	Validate    func(any) error
	Specificity int // 0=provider, 1=service, 2=resource
}

// Secret shapes per provider/kind/version

var CredentialRegistry = map[string]map[string]map[int]ProviderDef{
	"aws": {
		"aws_access_key": {
			1: {New: func() any { return &AWSCredential{} }, Validate: func(x any) error { return Validate.Struct(x) }},
		},
	},
	"cloudflare":   {"api_token": {1: {New: func() any { return &APIToken{} }, Validate: func(x any) error { return Validate.Struct(x) }}}},
	"hetzner":      {"api_token": {1: {New: func() any { return &APIToken{} }, Validate: func(x any) error { return Validate.Struct(x) }}}},
	"digitalocean": {"api_token": {1: {New: func() any { return &APIToken{} }, Validate: func(x any) error { return Validate.Struct(x) }}}},
	"generic": {
		"basic_auth": {1: {New: func() any { return &BasicAuth{} }, Validate: func(x any) error { return Validate.Struct(x) }}},
		"oauth2":     {1: {New: func() any { return &OAuth2Credential{} }, Validate: func(x any) error { return Validate.Struct(x) }}},
	},
}

// Scope shapes per provider/scopeKind/version

var ScopeRegistry = map[string]map[string]map[int]ScopeDef{
	"aws": {
		"provider": {1: {New: func() any { return &AWSProviderScope{} }, Validate: func(any) error { return nil }, Specificity: 0}},
		"service":  {1: {New: func() any { return &AWSServiceScope{} }, Validate: func(x any) error { return Validate.Struct(x) }, Specificity: 1}},
		"resource": {1: {New: func() any { return &AWSResourceScope{} }, Validate: func(x any) error { return Validate.Struct(x) }, Specificity: 2}},
	},
}

/*** API DTOs used by swagger ***/

// CreateCredentialRequest represents the POST /credentials payload
type CreateCredentialRequest struct {
	CredentialProvider string  `json:"credential_provider" validate:"required,oneof=aws cloudflare hetzner digitalocean generic"`
	Kind               string  `json:"kind" validate:"required"`                 // aws_access_key, api_token, basic_auth, oauth2
	SchemaVersion      int     `json:"schema_version" validate:"required,gte=1"` // secret schema version
	Name               string  `json:"name" validate:"omitempty,max=100"`        // human label
	ScopeKind          string  `json:"scope_kind" validate:"required,oneof=credential_provider service resource"`
	ScopeVersion       int     `json:"scope_version" validate:"required,gte=1"`        // scope schema version
	Scope              RawJSON `json:"scope" validate:"required" swaggertype:"object"` // {"service":"route53"} or {"arn":"..."}
	AccountID          string  `json:"account_id,omitempty" validate:"omitempty,max=32"`
	Region             string  `json:"region,omitempty" validate:"omitempty,max=32"`
	Secret             RawJSON `json:"secret" validate:"required" swaggertype:"object"` // encrypted later
}

// UpdateCredentialRequest represents PATCH /credentials/{id}
type UpdateCredentialRequest struct {
	Name         *string  `json:"name,omitempty"`
	AccountID    *string  `json:"account_id,omitempty"`
	Region       *string  `json:"region,omitempty"`
	ScopeKind    *string  `json:"scope_kind,omitempty"`
	ScopeVersion *int     `json:"scope_version,omitempty"`
	Scope        *RawJSON `json:"scope,omitempty" swaggertype:"object"`
	Secret       *RawJSON `json:"secret,omitempty" swaggertype:"object"` // set if rotating

}

// CredentialOut is what we return (no secrets)
type CredentialOut struct {
	ID                 string  `json:"id"`
	CredentialProvider string  `json:"credential_provider"`
	Kind               string  `json:"kind"`
	SchemaVersion      int     `json:"schema_version"`
	Name               string  `json:"name"`
	ScopeKind          string  `json:"scope_kind"`
	ScopeVersion       int     `json:"scope_version"`
	Scope              RawJSON `json:"scope" swaggertype:"object"`
	AccountID          string  `json:"account_id,omitempty"`
	Region             string  `json:"region,omitempty"`
	CreatedAt          string  `json:"created_at"`
	UpdatedAt          string  `json:"updated_at"`
}
