package sqlite

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

	"chat-switchboard/models"
	"chat-switchboard/store"
)

type MemoryStore struct{}

func NewMemoryStore() *MemoryStore { return &MemoryStore{} }

func (s *MemoryStore) Upsert(ctx context.Context, m *models.Memory) error {
	if m.ID == "" {
		m.ID = store.NewID()
	}
	if m.Status == "" {
		m.Status = models.MemoryStatusActive
	}
	if m.Confidence == 0 {
		m.Confidence = 1.0
	}

	_, err := DB.ExecContext(ctx, `
		INSERT INTO memories (id, scope, owner_id, user_id, key, value,
		                      source_channel_id, confidence, status)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON CONFLICT (scope, owner_id, COALESCE(user_id, '00000000-0000-0000-0000-000000000000'), key)
		DO UPDATE SET
			value             = excluded.value,
			confidence        = excluded.confidence,
			status            = excluded.status,
			source_channel_id = excluded.source_channel_id,
			updated_at        = datetime('now')
	`, m.ID, m.Scope, m.OwnerID, m.UserID, m.Key, m.Value,
		m.SourceChannelID, m.Confidence, m.Status)
	return err
}

func (s *MemoryStore) Update(ctx context.Context, m *models.Memory) error {
	_, err := DB.ExecContext(ctx, `
		UPDATE memories
		SET key = ?, value = ?, confidence = ?, status = ?,
		    source_channel_id = ?, updated_at = datetime('now')
		WHERE id = ?
	`, m.Key, m.Value, m.Confidence, m.Status, m.SourceChannelID, m.ID)
	return err
}

func (s *MemoryStore) GetByID(ctx context.Context, id string) (*models.Memory, error) {
	m := &models.Memory{}
	err := DB.QueryRowContext(ctx, `
		SELECT id, scope, owner_id, user_id, key, value,
		       source_channel_id, confidence, status, created_at, updated_at
		FROM memories WHERE id = ?
	`, id).Scan(
		&m.ID, &m.Scope, &m.OwnerID, &m.UserID, &m.Key, &m.Value,
		&m.SourceChannelID, &m.Confidence, &m.Status,
		st(&m.CreatedAt), st(&m.UpdatedAt),
	)
	if err == sql.ErrNoRows {
		return nil, fmt.Errorf("memory not found: %s", id)
	}
	return m, err
}

func (s *MemoryStore) List(ctx context.Context, f models.MemoryFilter) ([]models.Memory, error) {
	clauses := []string{"scope = ?", "owner_id = ?"}
	args := []interface{}{f.Scope, f.OwnerID}

	if f.UserID != nil {
		clauses = append(clauses, "user_id = ?")
		args = append(args, *f.UserID)
	}

	status := f.Status
	if status == "" {
		status = models.MemoryStatusActive
	}
	clauses = append(clauses, "status = ?")
	args = append(args, status)

	if f.Query != "" {
		// SQLite: LIKE-based keyword search (no tsvector)
		clauses = append(clauses, "(key || ' ' || value) LIKE ?")
		args = append(args, "%"+f.Query+"%")
	}

	limit := f.Limit
	if limit <= 0 || limit > 200 {
		limit = 50
	}
	args = append(args, limit)

	query := fmt.Sprintf(`
		SELECT id, scope, owner_id, user_id, key, value,
		       source_channel_id, confidence, status, created_at, updated_at
		FROM memories
		WHERE %s
		ORDER BY confidence DESC, updated_at DESC
		LIMIT ?
	`, strings.Join(clauses, " AND "))

	rows, err := DB.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	return s.scanMemories(rows)
}

func (s *MemoryStore) Delete(ctx context.Context, id string) error {
	_, err := DB.ExecContext(ctx, `DELETE FROM memories WHERE id = ?`, id)
	return err
}

func (s *MemoryStore) Archive(ctx context.Context, id string) error {
	_, err := DB.ExecContext(ctx, `
		UPDATE memories SET status = 'archived', updated_at = datetime('now') WHERE id = ?
	`, id)
	return err
}

func (s *MemoryStore) Recall(ctx context.Context, userID string, personaID *string, query string, limit int) ([]models.Memory, error) {
	if limit <= 0 || limit > 100 {
		limit = 20
	}

	// Build UNION query across applicable scopes
	parts := []string{}
	args := []interface{}{}

	// 1. User-scope memories
	userPart := `
		SELECT id, scope, owner_id, user_id, key, value,
		       source_channel_id, confidence, status, created_at, updated_at
		FROM memories
		WHERE scope = 'user' AND owner_id = ? AND status = 'active'
	`
	args = append(args, userID)

	if query != "" {
		userPart += " AND (key || ' ' || value) LIKE ?"
		args = append(args, "%"+query+"%")
	}
	parts = append(parts, userPart)

	// 2. Persona-scope memories
	if personaID != nil && *personaID != "" {
		personaPart := `
			SELECT id, scope, owner_id, user_id, key, value,
			       source_channel_id, confidence, status, created_at, updated_at
			FROM memories
			WHERE scope = 'persona' AND owner_id = ? AND status = 'active'
		`
		args = append(args, *personaID)
		if query != "" {
			personaPart += " AND (key || ' ' || value) LIKE ?"
			args = append(args, "%"+query+"%")
		}
		parts = append(parts, personaPart)

		// 3. Persona+user memories
		puPart := `
			SELECT id, scope, owner_id, user_id, key, value,
			       source_channel_id, confidence, status, created_at, updated_at
			FROM memories
			WHERE scope = 'persona_user' AND owner_id = ? AND user_id = ? AND status = 'active'
		`
		args = append(args, *personaID, userID)
		if query != "" {
			puPart += " AND (key || ' ' || value) LIKE ?"
			args = append(args, "%"+query+"%")
		}
		parts = append(parts, puPart)
	}

	fullQuery := fmt.Sprintf(`
		SELECT * FROM (%s)
		ORDER BY
			CASE scope
				WHEN 'persona_user' THEN 0
				WHEN 'persona'      THEN 1
				WHEN 'user'         THEN 2
			END,
			confidence DESC,
			updated_at DESC
		LIMIT ?
	`, strings.Join(parts, " UNION ALL "))
	args = append(args, limit)

	rows, err := DB.QueryContext(ctx, fullQuery, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	return s.scanMemories(rows)
}

func (s *MemoryStore) CountByOwner(ctx context.Context, scope, ownerID string) (int, error) {
	var count int
	err := DB.QueryRowContext(ctx, `
		SELECT COUNT(*) FROM memories
		WHERE scope = ? AND owner_id = ? AND status = 'active'
	`, scope, ownerID).Scan(&count)
	return count, err
}

func (s *MemoryStore) ListByStatus(ctx context.Context, status string, limit int) ([]models.Memory, error) {
	if limit <= 0 || limit > 500 {
		limit = 100
	}
	rows, err := DB.QueryContext(ctx, `
		SELECT id, scope, owner_id, user_id, key, value,
		       source_channel_id, confidence, status, created_at, updated_at
		FROM memories
		WHERE status = ?
		ORDER BY created_at DESC
		LIMIT ?
	`, status, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return s.scanMemories(rows)
}

// ── Helpers ─────────────────────────────────

func (s *MemoryStore) scanMemories(rows *sql.Rows) ([]models.Memory, error) {
	var memories []models.Memory
	for rows.Next() {
		var m models.Memory
		if err := rows.Scan(
			&m.ID, &m.Scope, &m.OwnerID, &m.UserID, &m.Key, &m.Value,
			&m.SourceChannelID, &m.Confidence, &m.Status,
			st(&m.CreatedAt), st(&m.UpdatedAt),
		); err != nil {
			return nil, err
		}
		memories = append(memories, m)
	}
	if memories == nil {
		memories = []models.Memory{}
	}
	return memories, rows.Err()
}

// ensure compile-time interface satisfaction
var _ store.MemoryStore = (*MemoryStore)(nil)

// ── CS5b additions (v0.29.0) ────────────────────────────────────────────

func (s *MemoryStore) SetEmbedding(ctx context.Context, id, embedding string) error {
	_, err := DB.ExecContext(ctx,
		`UPDATE memories SET embedding = ? WHERE id = ?`, embedding, id)
	return err
}

func (s *MemoryStore) GetLastExtractionMessageID(ctx context.Context, channelID, userID string) (string, error) {
	var lastID string
	err := DB.QueryRowContext(ctx,
		`SELECT last_message_id FROM memory_extraction_log WHERE channel_id = ? AND user_id = ?`,
		channelID, userID).Scan(&lastID)
	if err != nil {
		return "", nil
	}
	return lastID, nil
}

func (s *MemoryStore) UpsertExtractionLog(ctx context.Context, channelID, userID, lastMessageID string, count int) error {
	_, err := DB.ExecContext(ctx, `
		INSERT INTO memory_extraction_log (id, channel_id, user_id, last_message_id, memory_count)
		VALUES (?, ?, ?, ?, ?)
		ON CONFLICT(channel_id, user_id) DO UPDATE SET
			last_message_id = excluded.last_message_id,
			extracted_at = datetime('now'),
			memory_count = memory_extraction_log.memory_count + excluded.memory_count
	`, store.NewID(), channelID, userID, lastMessageID, count)
	return err
}

// ── v0.30.1 — Memory Compaction ────────────────────────────────────────

func (s *MemoryStore) DecayConfidence(ctx context.Context, olderThan string, decayFactor float64) (int, error) {
	res, err := DB.ExecContext(ctx, `
		UPDATE memories
		SET confidence = confidence * ?,
		    updated_at = datetime('now')
		WHERE status = 'active'
		  AND updated_at < ?
		  AND confidence > 0.1
	`, decayFactor, olderThan)
	if err != nil {
		return 0, err
	}
	n, _ := res.RowsAffected()
	return int(n), nil
}

func (s *MemoryStore) Prune(ctx context.Context, belowConfidence float64) (int, error) {
	res, err := DB.ExecContext(ctx, `
		UPDATE memories
		SET status = 'archived',
		    updated_at = datetime('now')
		WHERE status = 'active'
		  AND confidence < ?
	`, belowConfidence)
	if err != nil {
		return 0, err
	}
	n, _ := res.RowsAffected()
	return int(n), nil
}
