package ai

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"io"
	"time"

	"gorm.io/gorm/clause"
	"senlinai-agent/backend/internal/models"
)

type Gateway struct {
	systemKey        string
	encryptionSecret string
}

type SelectedKey struct {
	Provider string
	APIKey   string
	KeyType  string
}

func NewGateway(systemKey string) *Gateway {
	return NewGatewayWithSecret(systemKey, "development-ai-key-secret-change-me")
}

func NewGatewayWithSecret(systemKey string, encryptionSecret string) *Gateway {
	return &Gateway{systemKey: systemKey, encryptionSecret: encryptionSecret}
}

func (g *Gateway) SaveUserKey(userID uint, provider string, apiKey string) error {
	encrypted, err := encryptAPIKey(apiKey, g.encryptionSecret)
	if err != nil {
		return err
	}
	key := models.SenlinAgentAIKey{UserID: userID, Provider: provider, EncryptedAPIKey: encrypted}
	return models.DBService.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "user_id"}},
		DoUpdates: clause.AssignmentColumns([]string{"provider", "encrypted_api_key", "updated_at"}),
	}).Create(&key).Error
}

func (g *Gateway) SelectKey(userID uint) (SelectedKey, error) {
	var userKey models.SenlinAgentAIKey
	if err := models.DBService.Where("user_id = ?", userID).First(&userKey).Error; err == nil {
		apiKey, err := decryptAPIKey(userKey.EncryptedAPIKey, g.encryptionSecret)
		if err != nil {
			return SelectedKey{}, err
		}
		return SelectedKey{Provider: userKey.Provider, APIKey: apiKey, KeyType: "user"}, nil
	}
	if g.systemKey == "" {
		return SelectedKey{}, errors.New("no ai key available")
	}
	return SelectedKey{Provider: "openai", APIKey: g.systemKey, KeyType: "system"}, nil
}

func (g *Gateway) RecordCall(userID uint, provider string, usedKeyType string, action string, status string, errText string) error {
	return models.DBService.Create(&models.SenlinAgentAICallLog{
		UserID:      userID,
		Provider:    provider,
		UsedKeyType: usedKeyType,
		Action:      action,
		Status:      status,
		Error:       errText,
	}).Error
}

func (g *Gateway) CheckRateLimit(userID uint, action string, limit int, window time.Duration) error {
	if limit <= 0 {
		return nil
	}
	var count int64
	if err := models.DBService.Model(&models.SenlinAgentAICallLog{}).
		Where("user_id = ? AND action = ? AND created_at >= ?", userID, action, time.Now().Add(-window)).
		Count(&count).Error; err != nil {
		return err
	}
	if count >= int64(limit) {
		return errors.New("ai rate limit exceeded")
	}
	return nil
}

func encryptAPIKey(apiKey string, secret string) (string, error) {
	if apiKey == "" {
		return "", errors.New("api key is required")
	}
	block, err := aes.NewCipher(encryptionKey(secret))
	if err != nil {
		return "", err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return "", err
	}
	ciphertext := gcm.Seal(nil, nonce, []byte(apiKey), nil)
	return "v1:" + base64.RawURLEncoding.EncodeToString(append(nonce, ciphertext...)), nil
}

func decryptAPIKey(encrypted string, secret string) (string, error) {
	if len(encrypted) < 3 || encrypted[:3] != "v1:" {
		return encrypted, nil
	}
	raw, err := base64.RawURLEncoding.DecodeString(encrypted[3:])
	if err != nil {
		return "", errors.New("invalid encrypted api key")
	}
	block, err := aes.NewCipher(encryptionKey(secret))
	if err != nil {
		return "", err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}
	if len(raw) < gcm.NonceSize() {
		return "", errors.New("invalid encrypted api key")
	}
	nonce := raw[:gcm.NonceSize()]
	ciphertext := raw[gcm.NonceSize():]
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return "", errors.New("invalid encrypted api key")
	}
	return string(plaintext), nil
}

func encryptionKey(secret string) []byte {
	sum := sha256.Sum256([]byte(secret))
	return sum[:]
}
