package repository

import (
	"context"
	"sort"

	"apps/backend/internal/module/radar/domain"
)

func (m *Memory) SaveWatch(_ context.Context, w *domain.RadarWatch) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	if w == nil {
		return domain.ErrValidation
	}
	if err := w.NormalizeContext(); err != nil {
		return err
	}
	if old, ok := m.watches[w.ID]; ok && old.ContextMode == domain.WatchContextProduct && (old.BrandID != w.BrandID || old.ProductID != w.ProductID) {
		return domain.ErrValidation
	}
	cp := cloneWatch(w)
	m.watches[w.ID] = &cp
	return nil
}

func (m *Memory) GetWatch(_ context.Context, id string) (*domain.RadarWatch, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	w, ok := m.watches[id]
	if !ok {
		return nil, domain.ErrNotFound
	}
	cp := cloneWatch(w)
	if err := cp.NormalizeContext(); err != nil {
		return nil, err
	}
	return &cp, nil
}

func (m *Memory) DeleteWatch(_ context.Context, id string) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	if _, ok := m.watches[id]; !ok {
		return domain.ErrNotFound
	}
	delete(m.watches, id)
	return nil
}

func (m *Memory) ListWatches(_ context.Context, ownerUID int64, f domain.WatchListFilter) ([]*domain.RadarWatch, int64, error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	matched := make([]*domain.RadarWatch, 0, len(m.watches))
	for _, w := range m.watches {
		if w.OwnerUID != ownerUID {
			continue
		}
		if f.Status != "" && w.Status != f.Status {
			continue
		}
		cp := cloneWatch(w)
		if err := cp.NormalizeContext(); err != nil {
			return nil, 0, err
		}
		if f.ContextMode != "" && cp.ContextMode != f.ContextMode {
			continue
		}
		if f.BrandID != "" && cp.BrandID != f.BrandID {
			continue
		}
		if f.ProductID != "" && cp.ProductID != f.ProductID {
			continue
		}
		matched = append(matched, &cp)
	}
	sort.Slice(matched, func(i, j int) bool {
		if matched[i].CreatedAt != matched[j].CreatedAt {
			return matched[i].CreatedAt > matched[j].CreatedAt
		}
		return matched[i].ID < matched[j].ID
	})

	total := int64(len(matched))
	page, ps := f.Page, f.PageSize
	if page < 1 {
		page = 1
	}
	if ps < 1 {
		ps = 20
	}
	start := (page - 1) * ps
	if start >= len(matched) {
		return nil, total, nil
	}
	end := start + ps
	if end > len(matched) {
		end = len(matched)
	}
	return matched[start:end], total, nil
}

func (m *Memory) ListActiveWatches(_ context.Context, ownerUID int64) ([]*domain.RadarWatch, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	out := make([]*domain.RadarWatch, 0, len(m.watches))
	for _, w := range m.watches {
		if w.OwnerUID == ownerUID && w.Status == domain.WatchActive {
			cp := cloneWatch(w)
			if err := cp.NormalizeContext(); err != nil {
				return nil, err
			}
			out = append(out, &cp)
		}
	}
	sort.Slice(out, func(i, j int) bool {
		if out[i].CreatedAt != out[j].CreatedAt {
			return out[i].CreatedAt < out[j].CreatedAt
		}
		return out[i].ID < out[j].ID
	})
	return out, nil
}

func (m *Memory) ListAllActiveWatches(_ context.Context) ([]*domain.RadarWatch, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	out := make([]*domain.RadarWatch, 0, len(m.watches))
	for _, w := range m.watches {
		if w.Status == domain.WatchActive {
			cp := cloneWatch(w)
			if err := cp.NormalizeContext(); err != nil {
				return nil, err
			}
			out = append(out, &cp)
		}
	}
	sort.Slice(out, func(i, j int) bool {
		if out[i].OwnerUID != out[j].OwnerUID {
			return out[i].OwnerUID < out[j].OwnerUID
		}
		if out[i].CreatedAt != out[j].CreatedAt {
			return out[i].CreatedAt < out[j].CreatedAt
		}
		return out[i].ID < out[j].ID
	})
	return out, nil
}

func cloneWatch(w *domain.RadarWatch) domain.RadarWatch {
	cp := *w
	cp.Terms = append([]string(nil), w.Terms...)
	cp.ExcludeTerms = append([]string(nil), w.ExcludeTerms...)
	cp.Regions = append([]string(nil), w.Regions...)
	return cp
}

func (m *Memory) CountActiveWatches(_ context.Context, ownerUID int64) (int64, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	var n int64
	for _, w := range m.watches {
		if w.OwnerUID == ownerUID && w.Status == domain.WatchActive {
			n++
		}
	}
	return n, nil
}

func (m *Memory) TouchWatchSweptAt(_ context.Context, id string, at int64) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	w, ok := m.watches[id]
	if !ok {
		return domain.ErrNotFound
	}
	w.LastSweptAt = at
	return nil
}
