package jisho

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

	"chirami/internal/dict"
)

// BaseURL is the Jisho API endpoint for words search. It is exported so tests can
// point to an httptest server.
var BaseURL = "https://jisho.org/api/v1/search/words"

type apiItem struct {
	IsCommon bool     `json:"is_common"`
	Tags     []string `json:"tags"`
	JLPT     []string `json:"jlpt"`
	Japanese []struct {
		Word    string `json:"word"`
		Reading string `json:"reading"`
	} `json:"japanese"`
	Senses []struct {
		EnglishDefinitions []string `json:"english_definitions"`
		PartsOfSpeech      []string `json:"parts_of_speech"`
		Tags               []string `json:"tags"`
		Info               []string `json:"info"`
	} `json:"senses"`
}

type apiResponse struct {
	Data []apiItem `json:"data"`
}

func parseJLPT(tags []string) string {
	for _, t := range tags {
		lower := strings.ToLower(t)
		if strings.HasPrefix(lower, "jlpt-n") {
			return strings.ToUpper(strings.TrimPrefix(lower, "jlpt-"))
		}
	}
	return ""
}

// Search searches jisho.org for word, returning matching dictionary entries.
func Search(ctx context.Context, word string) ([]dict.Entry, error) {
	ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
	defer cancel()

	reqURL := fmt.Sprintf("%s?keyword=%s", BaseURL, url.QueryEscape(word))
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", "chirami/0.1")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("jisho: HTTP %d", resp.StatusCode)
	}

	var data apiResponse
	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
		return nil, err
	}

	entries := make([]dict.Entry, 0, len(data.Data))
	for _, item := range data.Data {
		var kanji []dict.Form
		var kana []dict.Form
		seenKanji := make(map[string]bool)
		seenKana := make(map[string]bool)

		for _, j := range item.Japanese {
			if j.Word != "" && !seenKanji[j.Word] {
				seenKanji[j.Word] = true
				kanji = append(kanji, dict.Form{Text: j.Word})
			}
			if j.Reading != "" && !seenKana[j.Reading] {
				seenKana[j.Reading] = true
				kana = append(kana, dict.Form{Text: j.Reading})
			}
		}

		if item.IsCommon {
			if len(kanji) > 0 {
				kanji[0].Common = true
			}
			if len(kana) > 0 {
				kana[0].Common = true
			}
		}

		senses := make([]dict.Sense, 0, len(item.Senses))
		for _, s := range item.Senses {
			senses = append(senses, dict.Sense{
				POS:   s.PartsOfSpeech,
				Misc:  s.Tags,
				Info:  s.Info,
				Gloss: s.EnglishDefinitions,
			})
		}

		entries = append(entries, dict.Entry{
			ID:     0,
			Kanji:  kanji,
			Kana:   kana,
			Senses: senses,
			JLPT:   parseJLPT(item.JLPT),
		})
	}

	return entries, nil
}
