package handler

import (
	"context"
	"fmt"
	"sync"
	"time"

	"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/models"
)

// mockAgentRepo implements repository.AgentRepo in-memory for testing.
type mockAgentRepo struct {
	mu sync.RWMutex
	m  map[string]models.AgentCardData
}

func newMockAgentRepo() *mockAgentRepo {
	return &mockAgentRepo{m: make(map[string]models.AgentCardData)}
}

func (r *mockAgentRepo) Create(ctx context.Context, a models.AgentCardData) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.m[a.ID]; ok {
		return fmt.Errorf("duplicate key: %s", a.ID)
	}
	r.m[a.ID] = a
	return nil
}

func (r *mockAgentRepo) Get(ctx context.Context, id string) (models.AgentCardData, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	a, ok := r.m[id]
	if !ok {
		return a, fmt.Errorf("not found: %s", id)
	}
	return a, nil
}

func (r *mockAgentRepo) List(ctx context.Context, statusFilter models.AgentStatus) ([]models.AgentCardData, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	result := make([]models.AgentCardData, 0, len(r.m))
	for _, a := range r.m {
		if statusFilter != "" && a.Status != statusFilter {
			continue
		}
		result = append(result, a)
	}
	return result, nil
}

func (r *mockAgentRepo) Update(ctx context.Context, id string, req models.UpdateAgentRequest) (models.AgentCardData, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	a, ok := r.m[id]
	if !ok {
		return a, fmt.Errorf("not found: %s", id)
	}
	if req.Status != nil {
		a.Status = *req.Status
	}
	if req.CurrentTask != nil {
		a.CurrentTask = req.CurrentTask
	}
	if req.TaskProgress != nil {
		a.TaskProgress = req.TaskProgress
	}
	if req.TaskElapsed != nil {
		a.TaskElapsed = req.TaskElapsed
	}
	if req.Channel != nil {
		a.Channel = *req.Channel
	}
	if req.ErrorMessage != nil {
		a.ErrorMessage = req.ErrorMessage
	}
	a.LastActivity = time.Now().UTC().Format(time.RFC3339)
	r.m[id] = a
	return a, nil
}

func (r *mockAgentRepo) Delete(ctx context.Context, id string) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.m[id]; !ok {
		return fmt.Errorf("not found: %s", id)
	}
	delete(r.m, id)
	return nil
}

func (r *mockAgentRepo) Count(ctx context.Context) (int, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return len(r.m), nil
}

// ─── Mock Session Repo ──────────────────────────────────────────────────────────

type mockSessionRepo struct {
	mu sync.RWMutex
	m  map[string]models.Session
}

func newMockSessionRepo() *mockSessionRepo {
	return &mockSessionRepo{m: make(map[string]models.Session)}
}

func (r *mockSessionRepo) Create(ctx context.Context, s models.Session) (models.Session, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if s.ID == "" {
		s.ID = fmt.Sprintf("sess-%d", len(r.m)+1)
	}
	if s.StartedAt.IsZero() {
		s.StartedAt = time.Now().UTC()
	}
	if s.LastActivityAt.IsZero() {
		s.LastActivityAt = s.StartedAt
	}
	r.m[s.ID] = s
	return s, nil
}

func (r *mockSessionRepo) ListActive(ctx context.Context) ([]models.Session, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	result := make([]models.Session, 0)
	for _, s := range r.m {
		if s.Status == "running" || s.Status == "streaming" {
			result = append(result, s)
		}
	}
	return result, nil
}

func (r *mockSessionRepo) Count(ctx context.Context) (int, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return len(r.m), nil
}

// ─── Mock Task Repo ─────────────────────────────────────────────────────────────

type mockTaskRepo struct {
	mu sync.RWMutex
	m  map[string]models.Task
}

func newMockTaskRepo() *mockTaskRepo {
	return &mockTaskRepo{m: make(map[string]models.Task)}
}

func (r *mockTaskRepo) Create(ctx context.Context, t models.Task) (models.Task, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if t.ID == "" {
		t.ID = fmt.Sprintf("task-%d", len(r.m)+1)
	}
	now := time.Now().UTC()
	if t.CreatedAt.IsZero() {
		t.CreatedAt = now
	}
	if t.UpdatedAt.IsZero() {
		t.UpdatedAt = now
	}
	r.m[t.ID] = t
	return t, nil
}

func (r *mockTaskRepo) ListRecent(ctx context.Context) ([]models.Task, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	result := make([]models.Task, 0, len(r.m))
	for _, t := range r.m {
		result = append(result, t)
	}
	return result, nil
}

func (r *mockTaskRepo) Count(ctx context.Context) (int, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return len(r.m), nil
}

// ─── Mock Project Repo ─────────────────────────────────────────────────────────

type mockProjectRepo struct {
	mu sync.RWMutex
	m  map[string]models.Project
}

func newMockProjectRepo() *mockProjectRepo {
	return &mockProjectRepo{m: make(map[string]models.Project)}
}

func (r *mockProjectRepo) Create(ctx context.Context, p models.Project) (models.Project, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if p.ID == "" {
		p.ID = fmt.Sprintf("proj-%d", len(r.m)+1)
	}
	now := time.Now().UTC()
	if p.CreatedAt.IsZero() {
		p.CreatedAt = now
	}
	if p.UpdatedAt.IsZero() {
		p.UpdatedAt = now
	}
	if p.AgentIDs == nil {
		p.AgentIDs = []string{}
	}
	r.m[p.ID] = p
	return p, nil
}

func (r *mockProjectRepo) List(ctx context.Context) ([]models.Project, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	result := make([]models.Project, 0, len(r.m))
	for _, p := range r.m {
		result = append(result, p)
	}
	return result, nil
}

func (r *mockProjectRepo) Count(ctx context.Context) (int, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return len(r.m), nil
}
