package service

import (
	"context"
	"net/http"
	"sort"
	"sync"
	"time"

	"cmr-backend/internal/apperr"
	"cmr-backend/internal/store/postgres"
)

type DevService struct {
	appEnv string
	store  *postgres.Store
	mu     sync.Mutex
	logSeq int64
	logs   []ClientDebugLogEntry
}

type ClientDebugLogEntry struct {
	ID          int64          `json:"id"`
	Source      string         `json:"source"`
	Level       string         `json:"level"`
	Category    string         `json:"category,omitempty"`
	Message     string         `json:"message"`
	EventID     string         `json:"eventId,omitempty"`
	ReleaseID   string         `json:"releaseId,omitempty"`
	SessionID   string         `json:"sessionId,omitempty"`
	ManifestURL string         `json:"manifestUrl,omitempty"`
	Route       string         `json:"route,omitempty"`
	OccurredAt  time.Time      `json:"occurredAt"`
	ReceivedAt  time.Time      `json:"receivedAt"`
	Details     map[string]any `json:"details,omitempty"`
}

type CreateClientDebugLogInput struct {
	Source      string         `json:"source"`
	Level       string         `json:"level"`
	Category    string         `json:"category"`
	Message     string         `json:"message"`
	EventID     string         `json:"eventId"`
	ReleaseID   string         `json:"releaseId"`
	SessionID   string         `json:"sessionId"`
	ManifestURL string         `json:"manifestUrl"`
	Route       string         `json:"route"`
	OccurredAt  string         `json:"occurredAt"`
	Details     map[string]any `json:"details"`
}

func NewDevService(appEnv string, store *postgres.Store) *DevService {
	return &DevService{
		appEnv: appEnv,
		store:  store,
	}
}

func (s *DevService) Enabled() bool {
	return s.appEnv != "production"
}

func (s *DevService) BootstrapDemo(ctx context.Context) (*postgres.DemoBootstrapSummary, error) {
	if !s.Enabled() {
		return nil, apperr.New(http.StatusNotFound, "not_found", "dev bootstrap is disabled")
	}
	return s.store.EnsureDemoData(ctx)
}

func (s *DevService) AddClientDebugLog(_ context.Context, input CreateClientDebugLogInput) (*ClientDebugLogEntry, error) {
	if !s.Enabled() {
		return nil, apperr.New(http.StatusNotFound, "not_found", "dev client logs are disabled")
	}
	if input.Message == "" {
		return nil, apperr.New(http.StatusBadRequest, "invalid_request", "message is required")
	}
	if input.Source == "" {
		input.Source = "unknown"
	}
	if input.Level == "" {
		input.Level = "info"
	}

	occurredAt := time.Now().UTC()
	if input.OccurredAt != "" {
		parsed, err := time.Parse(time.RFC3339, input.OccurredAt)
		if err != nil {
			return nil, apperr.New(http.StatusBadRequest, "invalid_request", "occurredAt must be RFC3339")
		}
		occurredAt = parsed.UTC()
	}

	entry := ClientDebugLogEntry{
		Source:      input.Source,
		Level:       input.Level,
		Category:    input.Category,
		Message:     input.Message,
		EventID:     input.EventID,
		ReleaseID:   input.ReleaseID,
		SessionID:   input.SessionID,
		ManifestURL: input.ManifestURL,
		Route:       input.Route,
		OccurredAt:  occurredAt,
		ReceivedAt:  time.Now().UTC(),
		Details:     input.Details,
	}

	s.mu.Lock()
	defer s.mu.Unlock()
	s.logSeq++
	entry.ID = s.logSeq
	s.logs = append(s.logs, entry)
	if len(s.logs) > 200 {
		s.logs = append([]ClientDebugLogEntry(nil), s.logs[len(s.logs)-200:]...)
	}
	copyEntry := entry
	return &copyEntry, nil
}

func (s *DevService) ListClientDebugLogs(_ context.Context, limit int) ([]ClientDebugLogEntry, error) {
	if !s.Enabled() {
		return nil, apperr.New(http.StatusNotFound, "not_found", "dev client logs are disabled")
	}
	if limit <= 0 || limit > 200 {
		limit = 50
	}

	s.mu.Lock()
	defer s.mu.Unlock()
	items := append([]ClientDebugLogEntry(nil), s.logs...)
	sort.Slice(items, func(i, j int) bool {
		return items[i].ID > items[j].ID
	})
	if len(items) > limit {
		items = items[:limit]
	}
	return items, nil
}

func (s *DevService) ClearClientDebugLogs(_ context.Context) error {
	if !s.Enabled() {
		return apperr.New(http.StatusNotFound, "not_found", "dev client logs are disabled")
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	s.logs = nil
	return nil
}
