package pota

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"
)

// hunterLogURL is the authenticated POTA user logbook endpoint (paginated).
// hunterOnly=1 restricts it to the user's chaser/hunter QSOs.
const hunterLogURL = "https://api.pota.app/user/logbook?hunterOnly=1&page=%d&size=%d"

// HunterQSO is one entry from the POTA hunter log: a contact the user made with
// a park activator, carrying the park reference to stamp onto the local QSO.
type HunterQSO struct {
	Worked    string    `json:"worked"`    // activator callsign (the station worked)
	Hunter    string    `json:"hunter"`    // YOUR callsign for this hunt (may vary: F4BPO, XV9Q…)
	Date      time.Time `json:"date"`      // QSO date/time (UTC)
	Band      string    `json:"band"`      // ADIF band, e.g. "20m"
	Mode      string    `json:"mode"`      // logged mode
	Reference string    `json:"reference"` // park ref, e.g. "US-2072"
}

// hunterEntry mirrors the POTA API logbook record (fields we use).
//
// IMPORTANT: POTA logbook entries come from the ACTIVATOR's uploaded log, so
// "station_callsign" is the activator (the park station you worked) and
// "worked_callsign" is YOU (the hunter, whom the activator worked). To match a
// local QSO — whose callsign field holds the activator — we key on
// station_callsign, NOT worked_callsign.
type hunterEntry struct {
	StationCallsign string `json:"station_callsign"` // the activator (park station)
	WorkedCallsign  string `json:"worked_callsign"`  // the hunter (you)
	QSODateTime     string `json:"qsoDateTime"`
	Band            string `json:"band"`
	LoggedMode      string `json:"loggedMode"`
	Reference       string `json:"reference"`
}

// FetchHunterLog downloads the user's entire POTA hunter log, page by page,
// using their pota.app session token as the Authorization header. logf may be
// nil. Returns a friendly error on an expired/invalid token (HTTP 401/403).
func FetchHunterLog(ctx context.Context, token string, logf func(string, ...any)) ([]HunterQSO, error) {
	token = strings.TrimSpace(token)
	if token == "" {
		return nil, fmt.Errorf("no POTA token — paste it from pota.app (DevTools → Network → Authorization header)")
	}
	client := &http.Client{Timeout: 30 * time.Second}
	const size = 100
	const maxPages = 5000 // safety bound

	var out []HunterQSO
	total := -1
	for page := 1; page <= maxPages; page++ {
		url := fmt.Sprintf(hunterLogURL, page, size)
		req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
		if err != nil {
			return nil, err
		}
		req.Header.Set("Authorization", token)
		req.Header.Set("Accept", "application/json")
		resp, err := client.Do(req)
		if err != nil {
			return nil, fmt.Errorf("POTA fetch: %w", err)
		}
		switch {
		case resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden:
			resp.Body.Close()
			return nil, fmt.Errorf("POTA rejected the token (HTTP %d) — it has likely expired; re-copy it from pota.app", resp.StatusCode)
		case resp.StatusCode != http.StatusOK:
			resp.Body.Close()
			return nil, fmt.Errorf("POTA HTTP %d", resp.StatusCode)
		}
		var body struct {
			Count   int           `json:"count"`
			Entries []hunterEntry `json:"entries"`
		}
		err = json.NewDecoder(resp.Body).Decode(&body)
		resp.Body.Close()
		if err != nil {
			return nil, fmt.Errorf("POTA decode: %w", err)
		}
		total = body.Count
		for _, e := range body.Entries {
			ref := strings.ToUpper(strings.TrimSpace(e.Reference))
			// The activator is the station we worked → station_callsign.
			act := strings.ToUpper(strings.TrimSpace(e.StationCallsign))
			if act == "" {
				act = strings.ToUpper(strings.TrimSpace(e.WorkedCallsign)) // fallback
			}
			if ref == "" || act == "" {
				continue
			}
			out = append(out, HunterQSO{
				Worked:    act,
				Hunter:    strings.ToUpper(strings.TrimSpace(e.WorkedCallsign)), // your call for this hunt
				Date:      parseHunterTime(e.QSODateTime),
				Band:      strings.ToLower(strings.TrimSpace(e.Band)),
				Mode:      strings.ToUpper(strings.TrimSpace(e.LoggedMode)),
				Reference: ref,
			})
		}
		if logf != nil {
			logf("pota: hunter log page %d (%d/%d)", page, len(out), total)
		}
		if len(body.Entries) == 0 || (total >= 0 && len(out) >= total) {
			break
		}
	}
	return out, nil
}

// parseHunterTime parses POTA's qsoDateTime, tolerating ISO variants (with/out
// 'T', timezone, or fractional seconds). POTA times are UTC.
func parseHunterTime(s string) time.Time {
	s = strings.TrimSpace(s)
	if s == "" {
		return time.Time{}
	}
	for _, layout := range []string{
		time.RFC3339Nano, time.RFC3339,
		"2006-01-02T15:04:05.000Z", "2006-01-02T15:04:05Z", "2006-01-02T15:04:05",
		"2006-01-02 15:04:05.000", "2006-01-02 15:04:05", "2006-01-02 15:04:05Z",
		"2006-01-02T15:04Z", "2006-01-02T15:04", "2006-01-02 15:04",
	} {
		if t, err := time.Parse(layout, s); err == nil {
			return t.UTC()
		}
	}
	return time.Time{}
}
