package automation

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/automation/dsl"
	"github.com/tidbcloud/AgentTeam/internal/core/secrets"
	automationstore "github.com/tidbcloud/AgentTeam/internal/store/automation"
	"gorm.io/gorm"
)

// AutomationSecretMetadata is the API-safe metadata for one repo-scoped secret.
type AutomationSecretMetadata struct {
	WorkspaceID     string
	RepositoryOwner string
	RepositoryName  string
	Name            string
	CreatedBy       string
	UpdatedBy       string
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

// AutomationSecretMutation carries plaintext secret material for immediate encryption.
type AutomationSecretMutation struct {
	RepositoryOwner string
	RepositoryName  string
	Name            string
	Value           string
}

// ListAutomationSecretsResult contains one API-safe page of automation secret metadata.
type ListAutomationSecretsResult struct {
	Secrets []AutomationSecretMetadata
	HasMore bool
}

// SetAutomationCredentialsKey configures AES-GCM storage for automation secrets.
func (s *AutomationsService) SetAutomationCredentialsKey(key []byte) {
	if s == nil {
		return
	}
	if len(key) == 0 {
		s.credentialsKey = nil
		return
	}
	s.credentialsKey = append([]byte(nil), key...)
}

// ListAutomationSecrets returns metadata for secrets in one repository scope.
func (s *AutomationsService) ListAutomationSecrets(ctx context.Context, workspaceID string, subject string, owner string, repo string, page int, perPage int) (ListAutomationSecretsResult, error) {
	if err := s.validateScope(workspaceID, subject); err != nil {
		return ListAutomationSecretsResult{}, err
	}
	owner, repo = normalizeAutomationSecretRepository(owner, repo)
	if err := validateAutomationRepository(owner, repo); err != nil {
		return ListAutomationSecretsResult{}, err
	}
	if err := s.automationAuthorizer().CanReadAutomationSecret(ctx, s.automationAuthorizationRequest(ctx, workspaceID, subject, AutomationAuthorizationRequest{RepositoryOwner: owner, RepositoryName: repo})); err != nil {
		return ListAutomationSecretsResult{}, err
	}
	page, perPage = normalizeAutomationPageWithMax(page, perPage, 100)
	result, err := s.store.ListAutomationSecrets(ctx, strings.TrimSpace(workspaceID), owner, repo, page, perPage)
	if err != nil {
		return ListAutomationSecretsResult{}, err
	}
	return ListAutomationSecretsResult{Secrets: automationSecretMetadataList(result.Secrets), HasMore: result.HasMore}, nil
}

// UpsertAutomationSecret encrypts and stores one repo-scoped automation secret.
func (s *AutomationsService) UpsertAutomationSecret(ctx context.Context, workspaceID string, subject string, input AutomationSecretMutation) (AutomationSecretMetadata, error) {
	if err := s.validateScope(workspaceID, subject); err != nil {
		return AutomationSecretMetadata{}, err
	}
	input = normalizeAutomationSecretMutation(input)
	if err := validateAutomationSecretMutation(input); err != nil {
		return AutomationSecretMetadata{}, err
	}
	if err := s.automationAuthorizer().CanManageAutomationSecret(ctx, s.automationAuthorizationRequest(ctx, workspaceID, subject, AutomationAuthorizationRequest{RepositoryOwner: input.RepositoryOwner, RepositoryName: input.RepositoryName})); err != nil {
		return AutomationSecretMetadata{}, err
	}
	if len(s.credentialsKey) != 32 {
		return AutomationSecretMetadata{}, invalidArgument("Automation secret storage is not configured on server")
	}
	codec, err := secrets.NewAESGCMCodec(s.credentialsKey)
	if err != nil {
		return AutomationSecretMetadata{}, invalidArgument("Automation secret storage is not configured on server")
	}
	ciphertext, err := codec.Seal(input.Value)
	if err != nil {
		return AutomationSecretMetadata{}, fmt.Errorf("seal automation secret: %w", err)
	}
	now := s.now()
	secret, err := s.store.UpsertAutomationSecret(ctx, automationstore.AutomationSecret{
		WorkspaceID:     strings.TrimSpace(workspaceID),
		RepositoryOwner: input.RepositoryOwner,
		RepositoryName:  input.RepositoryName,
		Name:            input.Name,
		ValueCiphertext: ciphertext,
		CreatedBy:       strings.TrimSpace(subject),
		UpdatedBy:       strings.TrimSpace(subject),
		CreatedAt:       now,
		UpdatedAt:       now,
	})
	if err != nil {
		return AutomationSecretMetadata{}, err
	}
	return automationSecretMetadata(secret), nil
}

// DeleteAutomationSecret removes one repo-scoped automation secret.
func (s *AutomationsService) DeleteAutomationSecret(ctx context.Context, workspaceID string, subject string, owner string, repo string, name string) error {
	if err := s.validateScope(workspaceID, subject); err != nil {
		return err
	}
	owner, repo = normalizeAutomationSecretRepository(owner, repo)
	name = strings.TrimSpace(name)
	if err := validateAutomationRepository(owner, repo); err != nil {
		return err
	}
	if err := validateAutomationSecretName(name); err != nil {
		return err
	}
	if err := s.automationAuthorizer().CanManageAutomationSecret(ctx, s.automationAuthorizationRequest(ctx, workspaceID, subject, AutomationAuthorizationRequest{RepositoryOwner: owner, RepositoryName: repo})); err != nil {
		return err
	}
	if err := s.store.DeleteAutomationSecret(ctx, strings.TrimSpace(workspaceID), owner, repo, name); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return notFound("Automation secret not found")
		}
		return err
	}
	return nil
}

func normalizeAutomationSecretMutation(input AutomationSecretMutation) AutomationSecretMutation {
	input.RepositoryOwner, input.RepositoryName = normalizeAutomationSecretRepository(input.RepositoryOwner, input.RepositoryName)
	input.Name = strings.TrimSpace(input.Name)
	return input
}

func normalizeAutomationSecretRepository(owner string, repo string) (string, string) {
	return strings.TrimSpace(owner), strings.TrimSpace(repo)
}

func validateAutomationSecretMutation(input AutomationSecretMutation) error {
	if err := validateAutomationRepository(input.RepositoryOwner, input.RepositoryName); err != nil {
		return err
	}
	if err := validateAutomationSecretName(input.Name); err != nil {
		return err
	}
	if input.Value == "" {
		return invalidArgument("Automation secret value is required")
	}
	return nil
}

func validateAutomationSecretName(name string) error {
	if name == "" || len(name) > 128 || !dsl.ValidEnvReferenceName(name) {
		return invalidArgument("Automation secret name must match [A-Za-z_][A-Za-z0-9_]* and be at most 128 bytes")
	}
	return nil
}

func automationSecretMetadataList(secrets []automationstore.AutomationSecret) []AutomationSecretMetadata {
	if len(secrets) == 0 {
		return nil
	}
	out := make([]AutomationSecretMetadata, 0, len(secrets))
	for _, secret := range secrets {
		out = append(out, automationSecretMetadata(secret))
	}
	return out
}

func automationSecretMetadata(secret automationstore.AutomationSecret) AutomationSecretMetadata {
	return AutomationSecretMetadata{
		WorkspaceID:     secret.WorkspaceID,
		RepositoryOwner: secret.RepositoryOwner,
		RepositoryName:  secret.RepositoryName,
		Name:            secret.Name,
		CreatedBy:       secret.CreatedBy,
		UpdatedBy:       secret.UpdatedBy,
		CreatedAt:       secret.CreatedAt,
		UpdatedAt:       secret.UpdatedAt,
	}
}
