package services

import (
	"context"
	"log"
	"time"

	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/db"
	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
	"go.mongodb.org/mongo-driver/v2/bson"
)

// legacyInstanceOIDC is the pre-0005 shape: one document per instance, in a
// collection this migration is the last thing to read.
type legacyInstanceOIDC struct {
	InstanceID      string    `bson:"instance_id"`
	Issuer          string    `bson:"issuer"`
	ClientID        string    `bson:"client_id"`
	ClientSecretEnc string    `bson:"client_secret_enc"`
	Enabled         bool      `bson:"enabled"`
	UpdatedAt       time.Time `bson:"updated_at"`
}

// MigrateAuthProviders copies each instance_oidc document into auth_providers.
//
// The ciphertext is copied verbatim rather than decrypted and re-encrypted: a
// migration that needs KEY_ENCRYPTION_KEY fails on an instance that has none
// and strands the SSO configuration it was supposed to preserve.
//
// instance_oidc is left in place and no longer read. Nothing deletes it — a
// migration that drops the only copy of a client secret has no undo.
func MigrateAuthProviders() error {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
	defer cancel()

	const marker = "0005_auth_providers"
	if n, _ := db.Col("migrations").CountDocuments(ctx, bson.M{"_id": marker}); n > 0 {
		return nil
	}

	cur, err := db.Col("instance_oidc").Find(ctx, bson.M{})
	if err != nil {
		return err
	}
	var legacy []legacyInstanceOIDC
	if err := cur.All(ctx, &legacy); err != nil {
		return err
	}

	migrated := 0
	for _, l := range legacy {
		if l.InstanceID == "" {
			continue
		}
		// Idempotent by skipping an instance that already has a provider, so a
		// re-run after a partial failure completes rather than duplicating.
		n, err := db.Col(authProviderCol).CountDocuments(ctx, bson.M{"instance_id": l.InstanceID})
		if err != nil {
			return err
		}
		if n > 0 {
			continue
		}
		providerID, err := randomProviderID()
		if err != nil {
			return err
		}
		now := time.Now()
		p := models.AuthProvider{
			InstanceID:      l.InstanceID,
			ProviderID:      providerID,
			Name:            "Single sign-on",
			Kind:            models.KindOIDC,
			Preset:          "",
			Issuer:          l.Issuer,
			ClientID:        l.ClientID,
			ClientSecretEnc: l.ClientSecretEnc,
			Scopes:          []string{"openid", "profile", "email"},
			Enabled:         l.Enabled,
			// This provider's redirect URI has changed and nobody has been told
			// yet. The settings card raises it until an administrator dismisses.
			CallbackNotice: true,
			Order:          0,
			CreatedAt:      now,
			UpdatedAt:      now,
		}
		if _, err := db.Col(authProviderCol).InsertOne(ctx, p); err != nil {
			return err
		}
		migrated++
	}
	if migrated > 0 {
		log.Printf("0005: migrated %d OIDC configuration(s) to auth_providers", migrated)
	}

	_, err = db.Col("migrations").InsertOne(ctx, bson.M{"_id": marker, "applied_at": time.Now()})
	return err
}
