package storage

import (
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"
)

const visitCooldown = 6 * time.Hour

type siteData struct {
	TotalVisits int `json:"totalVisits"`
}

type SiteStore struct {
	path         string
	mu           sync.Mutex
	recentVisits map[string]time.Time
}

func NewSiteStore(path string) (*SiteStore, error) {
	if err := ensureSiteFile(path); err != nil {
		return nil, err
	}
	return &SiteStore{
		path:         path,
		recentVisits: make(map[string]time.Time),
	}, nil
}

func (s *SiteStore) RecordVisit(fingerprint string) (int, bool, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	now := time.Now()
	s.cleanupRecentVisits(now)

	key := buildSiteVisitKey(fingerprint)
	if last, ok := s.recentVisits[key]; ok && now.Sub(last) < visitCooldown {
		data, err := s.read()
		if err != nil {
			return 0, false, err
		}
		return data.TotalVisits, false, nil
	}

	data, err := s.read()
	if err != nil {
		return 0, false, err
	}
	data.TotalVisits++
	s.recentVisits[key] = now
	if err := s.write(data); err != nil {
		return 0, false, err
	}
	return data.TotalVisits, true, nil
}

func (s *SiteStore) GetTotalVisits() (int, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	data, err := s.read()
	if err != nil {
		return 0, err
	}
	return data.TotalVisits, nil
}

func (s *SiteStore) read() (siteData, error) {
	bytes, err := os.ReadFile(s.path)
	if err != nil {
		return siteData{}, fmt.Errorf("read site data: %w", err)
	}
	var data siteData
	if err := json.Unmarshal(bytes, &data); err != nil {
		return siteData{}, fmt.Errorf("parse site data: %w", err)
	}
	return data, nil
}

func (s *SiteStore) write(data siteData) error {
	bytes, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return fmt.Errorf("encode site data: %w", err)
	}
	if err := os.WriteFile(s.path, bytes, 0o644); err != nil {
		return fmt.Errorf("write site data: %w", err)
	}
	return nil
}

func (s *SiteStore) cleanupRecentVisits(now time.Time) {
	for key, last := range s.recentVisits {
		if now.Sub(last) >= visitCooldown {
			delete(s.recentVisits, key)
		}
	}
}

func buildSiteVisitKey(fingerprint string) string {
	sum := sha256.Sum256([]byte("site|" + fingerprint))
	return fmt.Sprintf("%x", sum)
}

func ensureSiteFile(path string) error {
	dir := filepath.Dir(path)
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return fmt.Errorf("mkdir data dir: %w", err)
	}
	if _, err := os.Stat(path); err == nil {
		return nil
	} else if !os.IsNotExist(err) {
		return fmt.Errorf("stat site file: %w", err)
	}
	initial := siteData{TotalVisits: 0}
	bytes, err := json.MarshalIndent(initial, "", "  ")
	if err != nil {
		return fmt.Errorf("init site json: %w", err)
	}
	if err := os.WriteFile(path, bytes, 0o644); err != nil {
		return fmt.Errorf("write site json: %w", err)
	}
	return nil
}
