package com.tasteby.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * #357 웹 검색 추상화.
 * - Naver Search webkr.json 우선 (한국 식당 정확도 높음, 무료 일 25k).
 * - 키 미설정 또는 5xx/timeout 시 DDG HTML 파싱으로 폴백.
 * - 결과는 urlPatterns로 필터링 (기존 searchDuckDuckGo와 동일 인터페이스).
 */
@Service
public class WebSearchService {

    private static final Logger log = LoggerFactory.getLogger(WebSearchService.class);
    private static final int MAX_RESULTS = 5;

    private static final Duration REQ_TIMEOUT = Duration.ofSeconds(15);
    private static final HttpClient HTTP = HttpClient.newBuilder()
            .followRedirects(HttpClient.Redirect.NORMAL)
            .connectTimeout(Duration.ofSeconds(5))
            .build();

    private static final Pattern DDG_RESULT = Pattern.compile(
            "<a[^>]+class=\"result__a\"[^>]+href=\"([^\"]+)\"[^>]*>(.*?)</a>",
            Pattern.DOTALL);

    private final ObjectMapper json = new ObjectMapper();
    private final String naverClientId;
    private final String naverClientSecret;

    public WebSearchService(
            @Value("${app.naver.client-id:}") String naverClientId,
            @Value("${app.naver.client-secret:}") String naverClientSecret) {
        this.naverClientId = naverClientId == null ? "" : naverClientId.trim();
        this.naverClientSecret = naverClientSecret == null ? "" : naverClientSecret.trim();
        log.info("WebSearchService init — Naver={}", naverClientId.isEmpty() ? "off" : "on");
    }

    public List<Map<String, Object>> search(String query, String... urlPatterns) {
        if (!naverClientId.isEmpty() && !naverClientSecret.isEmpty()) {
            try {
                List<Map<String, Object>> n = searchNaver(query, urlPatterns);
                if (!n.isEmpty()) return n;
            } catch (Exception e) {
                log.warn("[NaverSearch] failed, falling back to DDG: {}", e.getMessage());
            }
        }
        try {
            return searchDdg(query, urlPatterns);
        } catch (Exception e) {
            log.warn("[DDG] failed: {}", e.getMessage());
            return List.of();
        }
    }

    // ─── Naver ───

    List<Map<String, Object>> searchNaver(String query, String... urlPatterns) throws Exception {
        String encoded = URLEncoder.encode(query, StandardCharsets.UTF_8);
        String url = "https://openapi.naver.com/v1/search/webkr.json?query=" + encoded + "&display=30";
        HttpRequest req = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .timeout(REQ_TIMEOUT)
                .header("X-Naver-Client-Id", naverClientId)
                .header("X-Naver-Client-Secret", naverClientSecret)
                .GET()
                .build();
        HttpResponse<String> resp = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
        if (resp.statusCode() >= 400) {
            throw new RuntimeException("Naver " + resp.statusCode());
        }
        JsonNode root = json.readTree(resp.body());
        JsonNode items = root.path("items");
        List<Map<String, Object>> out = new ArrayList<>();
        Set<String> seen = new HashSet<>();
        for (JsonNode it : items) {
            if (out.size() >= MAX_RESULTS) break;
            String link = it.path("link").asText("");
            String title = stripTags(it.path("title").asText(""));
            if (link.isEmpty() || !matchesPattern(link, urlPatterns)) continue;
            if (seen.add(link)) out.add(Map.of("title", title, "url", link));
        }
        log.info("[NaverSearch] '{}' → {}", query, out.size());
        return out;
    }

    // ─── DDG ───

    List<Map<String, Object>> searchDdg(String query, String... urlPatterns) throws Exception {
        String encoded = URLEncoder.encode(query, StandardCharsets.UTF_8);
        String url = "https://html.duckduckgo.com/html/?q=" + encoded;
        HttpRequest req = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .timeout(REQ_TIMEOUT)
                .header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
                .header("Accept", "text/html,application/xhtml+xml")
                .header("Accept-Language", "ko-KR,ko;q=0.9")
                .GET()
                .build();
        HttpResponse<String> resp = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
        String html = resp.body();
        Matcher m = DDG_RESULT.matcher(html);
        List<Map<String, Object>> out = new ArrayList<>();
        Set<String> seen = new HashSet<>();
        while (m.find() && out.size() < MAX_RESULTS) {
            String href = m.group(1);
            String title = m.group(2).replaceAll("<[^>]+>", "").trim();
            String actual = extractDdgUrl(href);
            if (actual == null || !matchesPattern(actual, urlPatterns)) continue;
            if (seen.add(actual)) out.add(Map.of("title", title, "url", actual));
        }
        log.info("[DDG] '{}' → {}", query, out.size());
        return out;
    }

    private String extractDdgUrl(String ddgHref) {
        try {
            if (ddgHref.contains("uddg=")) {
                String p = ddgHref.substring(ddgHref.indexOf("uddg=") + 5);
                int amp = p.indexOf('&');
                if (amp > 0) p = p.substring(0, amp);
                return URLDecoder.decode(p, StandardCharsets.UTF_8);
            }
            if (ddgHref.startsWith("http")) return ddgHref;
        } catch (Exception e) {
            log.debug("[DDG] url extract failed: {}", ddgHref);
        }
        return null;
    }

    static String stripTags(String s) {
        return s == null ? "" : s.replaceAll("<[^>]+>", "").trim();
    }

    static boolean matchesPattern(String url, String[] patterns) {
        if (patterns == null || patterns.length == 0) return true;
        for (String p : patterns) {
            if (url.contains(p)) return true;
        }
        return false;
    }
}
