package migration

import (
	"context"
	"fmt"
	"strings"
)

const adminConsentStatePrefix = "project:"

// MicrosoftAdminConsentRecord stores the outcome of a Microsoft admin consent redirect.
type MicrosoftAdminConsentRecord struct {
	TenantID         string
	ClientID         string
	ProjectID        string
	Granted          bool
	ErrorCode        string
	ErrorDescription string
}

// MicrosoftAdminConsent is a persisted tenant-level admin consent row.
type MicrosoftAdminConsent struct {
	TenantID         string  `json:"tenant_id"`
	ClientID         string  `json:"client_id"`
	ProjectID        string  `json:"project_id,omitempty"`
	Granted          bool    `json:"granted"`
	ErrorCode        string  `json:"error_code,omitempty"`
	ErrorDescription string  `json:"error_description,omitempty"`
	ConsentedAt      string  `json:"consented_at"`
	UpdatedAt        string  `json:"updated_at"`
}

func EncodeAdminConsentState(projectID string) string {
	projectID = strings.TrimSpace(projectID)
	if projectID == "" {
		return ""
	}
	return adminConsentStatePrefix + projectID
}

func ParseAdminConsentProjectID(state string) string {
	state = strings.TrimSpace(state)
	if !strings.HasPrefix(state, adminConsentStatePrefix) {
		return ""
	}
	return strings.TrimSpace(strings.TrimPrefix(state, adminConsentStatePrefix))
}

func (s *Service) RecordMicrosoftAdminConsent(ctx context.Context, in MicrosoftAdminConsentRecord) error {
	if s.db == nil {
		return fmt.Errorf("database not configured")
	}
	tenantID := strings.TrimSpace(in.TenantID)
	clientID := strings.TrimSpace(in.ClientID)
	projectID := strings.TrimSpace(in.ProjectID)
	if tenantID == "" {
		return fmt.Errorf("tenant id required")
	}
	if clientID == "" {
		return fmt.Errorf("client id required")
	}

	tx, err := s.db.Begin(ctx)
	if err != nil {
		return err
	}
	defer tx.Rollback(ctx)

	_, err = tx.Exec(ctx, `
		INSERT INTO migration_microsoft_admin_consents (
			tenant_id, client_id, project_id, granted, error_code, error_description
		) VALUES ($1, $2, NULLIF($3, '')::uuid, $4, $5, $6)
		ON CONFLICT (tenant_id, client_id) DO UPDATE SET
			project_id = COALESCE(EXCLUDED.project_id, migration_microsoft_admin_consents.project_id),
			granted = EXCLUDED.granted,
			error_code = EXCLUDED.error_code,
			error_description = EXCLUDED.error_description,
			consented_at = NOW(),
			updated_at = NOW()
	`, tenantID, clientID, projectID, in.Granted, in.ErrorCode, in.ErrorDescription)
	if err != nil {
		return err
	}

	if projectID != "" {
		if in.Granted {
			_, err = tx.Exec(ctx, `
				UPDATE migration_projects SET
					microsoft_tenant_id = $2,
					microsoft_admin_consent_at = NOW(),
					microsoft_admin_consent_error = '',
					updated_at = NOW()
				WHERE id = $1::uuid
			`, projectID, tenantID)
		} else {
			errMsg := strings.TrimSpace(in.ErrorDescription)
			if errMsg == "" {
				errMsg = strings.TrimSpace(in.ErrorCode)
			}
			_, err = tx.Exec(ctx, `
				UPDATE migration_projects SET
					microsoft_tenant_id = CASE
						WHEN microsoft_tenant_id = '' THEN $2
						ELSE microsoft_tenant_id
					END,
					microsoft_admin_consent_error = $3,
					updated_at = NOW()
				WHERE id = $1::uuid
			`, projectID, tenantID, errMsg)
		}
		if err != nil {
			return err
		}
	}

	return tx.Commit(ctx)
}

func (s *Service) ListMicrosoftAdminConsents(ctx context.Context) ([]MicrosoftAdminConsent, error) {
	rows, err := s.db.Query(ctx, `
		SELECT tenant_id, client_id, COALESCE(project_id::text, ''), granted,
		       error_code, error_description, consented_at::text, updated_at::text
		FROM migration_microsoft_admin_consents
		ORDER BY updated_at DESC
	`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var out []MicrosoftAdminConsent
	for rows.Next() {
		var row MicrosoftAdminConsent
		if err := rows.Scan(
			&row.TenantID, &row.ClientID, &row.ProjectID, &row.Granted,
			&row.ErrorCode, &row.ErrorDescription, &row.ConsentedAt, &row.UpdatedAt,
		); err != nil {
			return nil, err
		}
		out = append(out, row)
	}
	return out, rows.Err()
}
