package site

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"fmt"

	"github.com/gocql/gocql"
	"go.uber.org/zap"

	domainsite "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/site"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/security/apikey"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/security/ipcrypt"
)

// CreateSiteUseCase handles site creation business logic
type CreateSiteUseCase struct {
	repo         domainsite.Repository
	apiKeyGen    apikey.Generator
	apiKeyHasher apikey.Hasher
	ipEncryptor  *ipcrypt.IPEncryptor
	logger       *zap.Logger
}

// ProvideCreateSiteUseCase creates a new CreateSiteUseCase
func ProvideCreateSiteUseCase(
	repo domainsite.Repository,
	apiKeyGen apikey.Generator,
	apiKeyHasher apikey.Hasher,
	ipEncryptor *ipcrypt.IPEncryptor,
	logger *zap.Logger,
) *CreateSiteUseCase {
	return &CreateSiteUseCase{
		repo:         repo,
		apiKeyGen:    apiKeyGen,
		apiKeyHasher: apiKeyHasher,
		ipEncryptor:  ipEncryptor,
		logger:       logger,
	}
}

// CreateSiteInput is the input for creating a site
type CreateSiteInput struct {
	Domain    string
	SiteURL   string
	TestMode  bool   // true = generate test_sk_ key (skips verification)
	IPAddress string // Plain IP address (will be encrypted before storage)
}

// CreateSiteOutput is the output after creating a site
type CreateSiteOutput struct {
	ID                string `json:"id"`
	Domain            string `json:"domain"`
	SiteURL           string `json:"site_url"`
	APIKey            string `json:"api_key"` // ONLY shown once!
	VerificationToken string `json:"verification_token"`
	Status            string `json:"status"`
	SearchIndexName   string `json:"search_index_name"`
}

// Execute creates a new site
func (uc *CreateSiteUseCase) Execute(ctx context.Context, tenantID gocql.UUID, input *CreateSiteInput) (*CreateSiteOutput, error) {
	uc.logger.Info("executing create site use case",
		zap.String("tenant_id", tenantID.String()),
		zap.String("domain", input.Domain))

	// Generate API key (test or live based on test_mode)
	var apiKey string
	var err error
	if input.TestMode {
		apiKey, err = uc.apiKeyGen.GenerateTest() // test_sk_...
		uc.logger.Info("generating test API key for development")
	} else {
		apiKey, err = uc.apiKeyGen.Generate() // live_sk_...
	}
	if err != nil {
		uc.logger.Error("failed to generate API key", zap.Error(err))
		return nil, fmt.Errorf("failed to generate API key: %w", err)
	}

	// Hash API key
	apiKeyHash := uc.apiKeyHasher.Hash(apiKey)
	apiKeyPrefix := apikey.ExtractPrefix(apiKey)
	apiKeyLastFour := apikey.ExtractLastFour(apiKey)

	// Generate verification token
	verificationToken, err := generateVerificationToken()
	if err != nil {
		uc.logger.Error("failed to generate verification token", zap.Error(err))
		return nil, fmt.Errorf("failed to generate verification token: %w", err)
	}

	// Encrypt IP address (CWE-359: GDPR compliance)
	encryptedIP, err := uc.ipEncryptor.Encrypt(input.IPAddress)
	if err != nil {
		uc.logger.Error("failed to encrypt IP address",
			zap.String("domain", input.Domain),
			zap.Error(err))
		return nil, fmt.Errorf("failed to encrypt IP address: %w", err)
	}

	// Create site entity (no plan tier - usage-based billing)
	site := domainsite.NewSite(
		tenantID,
		input.Domain,
		input.SiteURL,
		apiKeyHash,
		apiKeyPrefix,
		apiKeyLastFour,
		encryptedIP,
	)
	site.VerificationToken = verificationToken

	// Check if domain already exists
	exists, err := uc.repo.DomainExists(ctx, input.Domain)
	if err != nil {
		uc.logger.Error("failed to check domain existence", zap.Error(err))
		return nil, fmt.Errorf("failed to check domain: %w", err)
	}
	if exists {
		uc.logger.Warn("domain already exists", zap.String("domain", input.Domain))
		return nil, domainsite.ErrDomainAlreadyExists
	}

	// Create in repository (writes to all 4 Cassandra tables)
	if err := uc.repo.Create(ctx, site); err != nil {
		uc.logger.Error("failed to create site", zap.Error(err))
		return nil, err
	}

	uc.logger.Info("site created successfully",
		zap.String("site_id", site.ID.String()),
		zap.String("domain", site.Domain))

	return &CreateSiteOutput{
		ID:                site.ID.String(),
		Domain:            site.Domain,
		SiteURL:           site.SiteURL,
		APIKey:            apiKey, // PLAINTEXT - only shown once!
		VerificationToken: verificationToken,
		Status:            site.Status,
		SearchIndexName:   site.SearchIndexName,
	}, nil
}

// generateVerificationToken generates a cryptographically secure verification token
func generateVerificationToken() (string, error) {
	b := make([]byte, 16) // 16 bytes = 128 bits
	if _, err := rand.Read(b); err != nil {
		return "", err
	}
	token := base64.RawURLEncoding.EncodeToString(b)
	return "mvp_" + token, nil // mvp = maplepress verify
}
