package site

import (
	"context"
	"fmt"
	"time"

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

	siteusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/site"
)

// RotateAPIKeyService handles API key rotation operations
type RotateAPIKeyService interface {
	RotateAPIKey(ctx context.Context, tenantID gocql.UUID, input *siteusecase.RotateAPIKeyInput) (*siteusecase.RotateAPIKeyOutput, error)
}

type rotateAPIKeyService struct {
	// Focused usecases
	getSiteUC                 *siteusecase.GetSiteUseCase
	generateAPIKeyUC          *siteusecase.GenerateAPIKeyUseCase
	updateSiteAPIKeyUC        *siteusecase.UpdateSiteAPIKeyUseCase
	updateSiteAPIKeyToRepoUC  *siteusecase.UpdateSiteAPIKeyToRepoUseCase

	logger *zap.Logger
}

// NewRotateAPIKeyService creates a new RotateAPIKeyService
func NewRotateAPIKeyService(
	getSiteUC *siteusecase.GetSiteUseCase,
	generateAPIKeyUC *siteusecase.GenerateAPIKeyUseCase,
	updateSiteAPIKeyUC *siteusecase.UpdateSiteAPIKeyUseCase,
	updateSiteAPIKeyToRepoUC *siteusecase.UpdateSiteAPIKeyToRepoUseCase,
	logger *zap.Logger,
) RotateAPIKeyService {
	return &rotateAPIKeyService{
		getSiteUC:                getSiteUC,
		generateAPIKeyUC:         generateAPIKeyUC,
		updateSiteAPIKeyUC:       updateSiteAPIKeyUC,
		updateSiteAPIKeyToRepoUC: updateSiteAPIKeyToRepoUC,
		logger:                   logger.Named("rotate-apikey-service"),
	}
}

// RotateAPIKey orchestrates the API key rotation workflow
func (s *rotateAPIKeyService) RotateAPIKey(ctx context.Context, tenantID gocql.UUID, input *siteusecase.RotateAPIKeyInput) (*siteusecase.RotateAPIKeyOutput, error) {
	s.logger.Info("rotating API key",
		zap.String("tenant_id", tenantID.String()),
		zap.String("site_id", input.SiteID))

	// Step 1: Get current site
	siteOutput, err := s.getSiteUC.Execute(ctx, tenantID, &siteusecase.GetSiteInput{
		ID: input.SiteID,
	})
	if err != nil {
		s.logger.Error("failed to get site",
			zap.String("site_id", input.SiteID),
			zap.Error(err))
		return nil, err
	}
	site := siteOutput.Site

	// Step 2: Store old key info for response and rotation
	oldKeyLastFour := site.APIKeyLastFour
	oldAPIKeyHash := site.APIKeyHash

	// Step 3: Determine test mode from existing API key prefix
	// If current key starts with "test_", generate a test key; otherwise generate live key
	testMode := len(site.APIKeyPrefix) >= 5 && site.APIKeyPrefix[:5] == "test_"

	s.logger.Info("generating new API key",
		zap.Bool("test_mode", testMode),
		zap.String("current_key_prefix", site.APIKeyPrefix),
		zap.String("site_id", input.SiteID))
	apiKeyResult, err := s.generateAPIKeyUC.Execute(testMode)
	if err != nil {
		s.logger.Error("failed to generate new API key", zap.Error(err))
		return nil, fmt.Errorf("failed to generate API key: %w", err)
	}

	// Step 4: Update site entity with new key details
	s.updateSiteAPIKeyUC.Execute(&siteusecase.UpdateSiteAPIKeyInput{
		Site:           site,
		NewAPIKeyHash:  apiKeyResult.HashedKey,
		NewKeyPrefix:   apiKeyResult.Prefix,
		NewKeyLastFour: apiKeyResult.LastFour,
	})

	// Step 5: Update site API key in repository (all tables)
	// Use UpdateSiteAPIKeyToRepoUC to properly handle sites_by_apikey table (delete old + insert new)
	if err := s.updateSiteAPIKeyToRepoUC.Execute(ctx, &siteusecase.UpdateSiteAPIKeyToRepoInput{
		Site:          site,
		OldAPIKeyHash: oldAPIKeyHash,
	}); err != nil {
		s.logger.Error("failed to update site with new API key", zap.Error(err))
		return nil, err
	}

	// Step 6: Build output
	rotatedAt := time.Now()

	s.logger.Info("API key rotated successfully",
		zap.String("site_id", input.SiteID),
		zap.String("old_key_last_four", oldKeyLastFour),
		zap.String("new_key_prefix", apiKeyResult.Prefix),
		zap.String("new_key_last_four", apiKeyResult.LastFour))

	return &siteusecase.RotateAPIKeyOutput{
		NewAPIKey:      apiKeyResult.PlaintextKey, // PLAINTEXT - only shown once!
		OldKeyLastFour: oldKeyLastFour,
		RotatedAt:      rotatedAt,
	}, nil
}
