package kb

import (
	"context"
	"fmt"
	"strings"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

// KnowledgeBaseSetupSession is the durable editing cursor for a draft or an
// isolated Official working revision. Step readiness is derived by the service
// from KB, source, revision, and artifact state rather than persisted here.
type KnowledgeBaseSetupSession struct {
	ID                    string     `gorm:"primaryKey;size:64"`
	WorkspaceID           string     `gorm:"size:64;not null;uniqueIndex:uk_kb_setup_session_kb;index:idx_kb_setup_sessions_workspace_updated"`
	KnowledgeBaseID       string     `gorm:"size:64;not null;uniqueIndex:uk_kb_setup_session_kb"`
	Mode                  string     `gorm:"size:32;not null"`
	ActiveStep            string     `gorm:"size:32;not null;default:'basic'"`
	WorkingRevision       string     `gorm:"size:64;not null"`
	PublishedRevision     string     `gorm:"size:64;not null;default:''"`
	WorkingSnapshotJSON   string     `gorm:"type:text;not null"`
	PublishedSnapshotJSON string     `gorm:"type:text;not null"`
	SaveRequestID         string     `gorm:"size:128;not null;default:''"`
	Version               int64      `gorm:"not null;default:1"`
	CreatedBy             string     `gorm:"size:64;not null"`
	UpdatedBy             string     `gorm:"size:64;not null"`
	CreatedAt             time.Time  `gorm:"not null"`
	UpdatedAt             time.Time  `gorm:"not null;index:idx_kb_setup_sessions_workspace_updated"`
	CompletedAt           *time.Time `gorm:"index"`
}

// KnowledgeBaseSetupSummary is the bounded collection projection needed to
// resume a Draft without loading its sources or artifact state on first paint.
type KnowledgeBaseSetupSummary struct {
	Mode       string
	ActiveStep string
	Version    int64
}

func (KnowledgeBaseSetupSession) TableName() string {
	return "knowledge_base_setup_sessions"
}

// KnowledgeBaseSetupSourceArtifact stores only an explicit user disposition
// for an optional setup artifact. Ready state remains derived from the current
// source revision, so a stale preference cannot override published truth.
type KnowledgeBaseSetupSourceArtifact struct {
	ID              string    `gorm:"primaryKey;size:64"`
	WorkspaceID     string    `gorm:"size:64;not null;uniqueIndex:uk_kb_setup_source_artifact"`
	KnowledgeBaseID string    `gorm:"size:64;not null;uniqueIndex:uk_kb_setup_source_artifact"`
	SourceID        string    `gorm:"size:64;not null;uniqueIndex:uk_kb_setup_source_artifact"`
	Kind            string    `gorm:"size:32;not null;uniqueIndex:uk_kb_setup_source_artifact"`
	State           string    `gorm:"size:32;not null"`
	UpdatedBy       string    `gorm:"size:64;not null"`
	CreatedAt       time.Time `gorm:"not null"`
	UpdatedAt       time.Time `gorm:"not null"`
}

func (KnowledgeBaseSetupSourceArtifact) TableName() string {
	return "knowledge_base_setup_source_artifacts"
}

// KnowledgeBaseGenerationRequest is the durable, source-revision-fenced
// control-plane record for an optional generated setup artifact. Generic
// Approval remains owned by AgentTeam; ApprovalID links a Member request to that
// authority, while an empty ApprovalID identifies an authorized direct Admin
// request.
type KnowledgeBaseGenerationRequest struct {
	ID                  string     `gorm:"primaryKey;size:64;index:idx_kb_generation_terminal_projection,priority:4"`
	WorkspaceID         string     `gorm:"size:64;not null;index:idx_kb_generation_requests_source,priority:1;index:idx_kb_generation_terminal_projection,priority:2;uniqueIndex:uk_kb_generation_request_replay,priority:1"`
	KnowledgeBaseID     string     `gorm:"size:64;not null;index:idx_kb_generation_requests_source,priority:2"`
	SourceID            string     `gorm:"size:64;not null;index:idx_kb_generation_requests_source,priority:3"`
	Kind                string     `gorm:"size:32;not null;index:idx_kb_generation_requests_source,priority:4"`
	SourceRevisionID    string     `gorm:"size:64;not null"`
	SourceCommitSHA     string     `gorm:"size:128;not null"`
	Scope               string     `gorm:"size:64;not null"`
	Language            string     `gorm:"size:32;not null"`
	Reason              string     `gorm:"type:text;not null"`
	RequesterID         string     `gorm:"size:64;not null;uniqueIndex:uk_kb_generation_request_replay,priority:2"`
	ApprovalID          string     `gorm:"size:64;not null;default:'';index"`
	ApprovalVersion     int64      `gorm:"not null;default:0"`
	Status              string     `gorm:"size:32;not null;index:idx_kb_generation_requests_source,priority:5"`
	JobID               string     `gorm:"size:64;not null;default:'';index"`
	ArtifactRevision    string     `gorm:"size:128;not null;default:''"`
	IdempotencyKey      string     `gorm:"size:128;not null;uniqueIndex:uk_kb_generation_request_replay,priority:3"`
	PayloadDigest       string     `gorm:"size:64;not null"`
	Decision            string     `gorm:"size:32;not null;default:''"`
	DecisionID          string     `gorm:"size:128;not null;default:''"`
	DecisionInputReason string     `gorm:"type:text;not null"`
	DecisionReason      string     `gorm:"type:text;not null"`
	DecidedBy           string     `gorm:"size:64;not null;default:''"`
	Version             int64      `gorm:"not null;default:1"`
	NotificationState   string     `gorm:"size:32;not null;default:'';index:idx_kb_generation_terminal_projection,priority:1"`
	NotificationVersion int64      `gorm:"not null;default:0"`
	CreatedAt           time.Time  `gorm:"not null"`
	UpdatedAt           time.Time  `gorm:"not null;index:idx_kb_generation_requests_source,priority:6;index:idx_kb_generation_terminal_projection,priority:3"`
	StartedAt           *time.Time `gorm:"index"`
	FinishedAt          *time.Time `gorm:"index"`
	DecidedAt           *time.Time `gorm:"index"`
	NotifiedAt          *time.Time
}

func (KnowledgeBaseGenerationRequest) TableName() string {
	return "knowledge_base_generation_requests"
}

func (s *Store) CreateKnowledgeBaseSetupSession(ctx context.Context, session KnowledgeBaseSetupSession) (KnowledgeBaseSetupSession, error) {
	if err := s.ensureDB(); err != nil {
		return KnowledgeBaseSetupSession{}, err
	}
	if err := s.db.WithContext(ctx).Create(&session).Error; err != nil {
		return KnowledgeBaseSetupSession{}, fmt.Errorf("create knowledge base setup session: %w", err)
	}
	return session, nil
}

func (s *Store) GetKnowledgeBaseSetupSession(ctx context.Context, workspaceID, kbID string) (KnowledgeBaseSetupSession, error) {
	if err := s.ensureDB(); err != nil {
		return KnowledgeBaseSetupSession{}, err
	}
	var session KnowledgeBaseSetupSession
	err := s.db.WithContext(ctx).
		Where("workspace_id = ? AND knowledge_base_id = ?", strings.TrimSpace(workspaceID), strings.TrimSpace(kbID)).
		First(&session).Error
	if err != nil {
		return KnowledgeBaseSetupSession{}, err
	}
	return session, nil
}

func (s *Store) ListKnowledgeBaseSetupSessions(ctx context.Context, workspaceID string, kbIDs []string) ([]KnowledgeBaseSetupSession, error) {
	if err := s.ensureDB(); err != nil {
		return nil, err
	}
	ids := uniqueTrimmedStrings(kbIDs)
	if len(ids) == 0 {
		return []KnowledgeBaseSetupSession{}, nil
	}
	var sessions []KnowledgeBaseSetupSession
	if err := s.db.WithContext(ctx).
		Select("knowledge_base_id", "mode", "active_step", "version").
		Where("workspace_id = ? AND knowledge_base_id IN ?", strings.TrimSpace(workspaceID), ids).
		Find(&sessions).Error; err != nil {
		return nil, fmt.Errorf("list knowledge base setup sessions: %w", err)
	}
	return sessions, nil
}

// UpdateKnowledgeBaseSetupStep advances one session with an exact version
// fence. It returns false when another device has already updated the session.
func (s *Store) UpdateKnowledgeBaseSetupStep(ctx context.Context, workspaceID, kbID, activeStep, updatedBy string, expectedVersion int64, updatedAt time.Time) (KnowledgeBaseSetupSession, bool, error) {
	if err := s.ensureDB(); err != nil {
		return KnowledgeBaseSetupSession{}, false, err
	}
	result := s.db.WithContext(ctx).Model(&KnowledgeBaseSetupSession{}).
		Where("workspace_id = ? AND knowledge_base_id = ? AND version = ?", strings.TrimSpace(workspaceID), strings.TrimSpace(kbID), expectedVersion).
		Updates(map[string]any{
			"active_step": activeStep,
			"updated_by":  strings.TrimSpace(updatedBy),
			"updated_at":  updatedAt,
			"version":     gorm.Expr("version + 1"),
		})
	if result.Error != nil {
		return KnowledgeBaseSetupSession{}, false, fmt.Errorf("update knowledge base setup session: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return KnowledgeBaseSetupSession{}, false, nil
	}
	session, err := s.GetKnowledgeBaseSetupSession(ctx, workspaceID, kbID)
	return session, true, err
}

func (s *Store) ListKnowledgeBaseSetupSourceArtifacts(ctx context.Context, workspaceID, kbID string, sourceIDs []string) ([]KnowledgeBaseSetupSourceArtifact, error) {
	if err := s.ensureDB(); err != nil {
		return nil, err
	}
	ids := uniqueTrimmedStrings(sourceIDs)
	if len(ids) == 0 {
		return []KnowledgeBaseSetupSourceArtifact{}, nil
	}
	var rows []KnowledgeBaseSetupSourceArtifact
	if err := s.db.WithContext(ctx).
		Where("workspace_id = ? AND knowledge_base_id = ? AND source_id IN ?", strings.TrimSpace(workspaceID), strings.TrimSpace(kbID), ids).
		Find(&rows).Error; err != nil {
		return nil, fmt.Errorf("list knowledge base setup source artifacts: %w", err)
	}
	return rows, nil
}

func (s *Store) UpsertKnowledgeBaseSetupSourceArtifact(ctx context.Context, artifact KnowledgeBaseSetupSourceArtifact) (KnowledgeBaseSetupSourceArtifact, error) {
	if err := s.ensureDB(); err != nil {
		return KnowledgeBaseSetupSourceArtifact{}, err
	}
	err := s.db.WithContext(ctx).Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "workspace_id"}, {Name: "knowledge_base_id"}, {Name: "source_id"}, {Name: "kind"}},
		DoUpdates: clause.AssignmentColumns([]string{"state", "updated_by", "updated_at"}),
	}).Create(&artifact).Error
	if err != nil {
		return KnowledgeBaseSetupSourceArtifact{}, fmt.Errorf("upsert knowledge base setup source artifact: %w", err)
	}
	var stored KnowledgeBaseSetupSourceArtifact
	if err := s.db.WithContext(ctx).
		Where("workspace_id = ? AND knowledge_base_id = ? AND source_id = ? AND kind = ?", artifact.WorkspaceID, artifact.KnowledgeBaseID, artifact.SourceID, artifact.Kind).
		First(&stored).Error; err != nil {
		return KnowledgeBaseSetupSourceArtifact{}, err
	}
	return stored, nil
}
