package repository

import (
	"baron-sso-backend/internal/domain"
	"context"
	"errors"

	"gorm.io/gorm"
)

type ClientConsentRepository interface {
	Upsert(ctx context.Context, consent *domain.ClientConsent) error
	Delete(ctx context.Context, subject, clientID string) error
	DeleteByClient(ctx context.Context, clientID string) error
	List(ctx context.Context, clientID string, limit, offset int) ([]domain.ClientConsentWithTenantInfo, int64, error)
	ListByTenant(ctx context.Context, clientID, tenantID string, limit, offset int) ([]domain.ClientConsentWithTenantInfo, int64, error)
	ListBySubject(ctx context.Context, subject string) ([]domain.ClientConsent, error)
	ListSubjectsByClient(ctx context.Context, clientID string) ([]string, error)
	Find(ctx context.Context, clientID, subject string) (*domain.ClientConsent, error)
}

type clientConsentRepo struct {
	db *gorm.DB
}

func NewClientConsentRepository(db *gorm.DB) ClientConsentRepository {
	return &clientConsentRepo{db: db}
}

func (r *clientConsentRepo) Find(ctx context.Context, clientID, subject string) (*domain.ClientConsent, error) {
	var consent domain.ClientConsent
	err := r.db.WithContext(ctx).
		Where("client_id = ? AND subject = ?", clientID, subject).
		First(&consent).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return &consent, nil
}

func (r *clientConsentRepo) Upsert(ctx context.Context, consent *domain.ClientConsent) error {
	return r.db.WithContext(ctx).Unscoped().
		Where("client_id = ? AND subject = ?", consent.ClientID, consent.Subject).
		Assign(map[string]interface{}{
			"granted_scopes": consent.GrantedScopes,
			"updated_at":     gorm.Expr("NOW()"),
			"deleted_at":     nil,
		}).
		FirstOrCreate(consent).Error
}

func (r *clientConsentRepo) Delete(ctx context.Context, subject, clientID string) error {
	return r.db.WithContext(ctx).
		Where("subject = ? AND client_id = ?", subject, clientID).
		Delete(&domain.ClientConsent{}).Error
}

func (r *clientConsentRepo) DeleteByClient(ctx context.Context, clientID string) error {
	return r.db.WithContext(ctx).
		Where("client_id = ?", clientID).
		Delete(&domain.ClientConsent{}).Error
}

func (r *clientConsentRepo) List(ctx context.Context, clientID string, limit, offset int) ([]domain.ClientConsentWithTenantInfo, int64, error) {
	var consents []domain.ClientConsentWithTenantInfo
	var total int64

	// Base query for counting
	countQuery := r.db.WithContext(ctx).Unscoped().Model(&domain.ClientConsent{}).Where("client_id = ?", clientID)
	if err := countQuery.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// Query for fetching data
	query := r.db.WithContext(ctx).Unscoped().
		Model(&domain.ClientConsent{}).
		Select("client_consents.*, users.tenant_id, tenants.name as tenant_name").
		Joins("LEFT JOIN users ON users.id::text = client_consents.subject").
		Joins("LEFT JOIN tenants ON tenants.id = users.tenant_id").
		Where("client_consents.client_id = ?", clientID)

	err := query.Limit(limit).Offset(offset).Order("client_consents.updated_at DESC").Scan(&consents).Error
	return consents, total, err
}

func (r *clientConsentRepo) ListByTenant(ctx context.Context, clientID, tenantID string, limit, offset int) ([]domain.ClientConsentWithTenantInfo, int64, error) {
	var consents []domain.ClientConsentWithTenantInfo
	var total int64

	// Base query for counting
	countQuery := r.db.WithContext(ctx).Unscoped().
		Model(&domain.ClientConsent{}).
		Joins("JOIN users ON users.id::text = client_consents.subject").
		Where("client_consents.client_id = ? AND users.tenant_id = ?", clientID, tenantID)

	if err := countQuery.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// Query for fetching data
	query := r.db.WithContext(ctx).Unscoped().
		Model(&domain.ClientConsent{}).
		Select("client_consents.*, users.tenant_id, tenants.name as tenant_name").
		Joins("JOIN users ON users.id::text = client_consents.subject").
		Joins("JOIN tenants ON tenants.id = users.tenant_id").
		Where("client_consents.client_id = ? AND users.tenant_id = ?", clientID, tenantID)

	err := query.
		Limit(limit).
		Offset(offset).
		Order("client_consents.updated_at DESC").
		Scan(&consents).Error

	return consents, total, err
}

func (r *clientConsentRepo) ListBySubject(ctx context.Context, subject string) ([]domain.ClientConsent, error) {
	var consents []domain.ClientConsent
	err := r.db.WithContext(ctx).Unscoped().
		Where("subject = ?", subject).
		Order("updated_at DESC").
		Find(&consents).Error
	return consents, err
}

func (r *clientConsentRepo) ListSubjectsByClient(ctx context.Context, clientID string) ([]string, error) {
	var subjects []string
	err := r.db.WithContext(ctx).Unscoped().
		Model(&domain.ClientConsent{}).
		Distinct("subject").
		Where("client_id = ?", clientID).
		Order("subject ASC").
		Pluck("subject", &subjects).Error
	return subjects, err
}
