package incheon.cmm.g2f.search.web;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.*;

@Slf4j
@RestController
@RequestMapping("/api/v1/kakao/search")
public class KakaoSearchController {

    @Value("${kakao.rest-api-key:}")
    private String kakaoApiKey;

    @Value("${kakao.api.keyword-url:https://dapi.kakao.com/v2/local/search/keyword.json}")
    private String keywordApiUrl;

    @Value("${kakao.api.address-url:https://dapi.kakao.com/v2/local/search/address.json}")
    private String addressApiUrl;

    /** 인천광역시 바운딩박스 (좌측X, 좌측Y, 우측X, 우측Y) */
    private static final String INCHEON_RECT = "123.83165,36.77302,126.80074,38.01028";

    /** 검색 쿼리에 자동 추가할 지역명 */
    private static final String REGION_PREFIX = "인천";

    @Autowired private RestTemplate restTemplate;

    /**
     * 카카오 키워드 장소 검색 프록시
     * - 첫 요청 시 is_end=true까지 전체 조회하여 실제 건수 확보 (최대 45건, 3회 호출)
     * - offset/limit 기반으로 슬라이싱하여 반환
     */
    @GetMapping("/keyword")
    public ResponseEntity<Map<String, Object>> searchByKeyword(
            @RequestParam String query,
            @RequestParam(defaultValue = "15") int limit,
            @RequestParam(defaultValue = "0") int offset,
            @RequestParam(defaultValue = "false") boolean fetchAll) {

        String regionQuery = query.startsWith(REGION_PREFIX) ? query : REGION_PREFIX + " " + query;
        log.info("[카카오검색] 키워드 요청: query='{}' → '{}', limit={}, offset={}, fetchAll={}", query, regionQuery, limit, offset, fetchAll);

        List<Map<String, Object>> allHits = fetchAllFromKakao(regionQuery);

        // 키워드 검색 결과가 0건이면 주소 검색으로 폴백
        if (allHits.isEmpty()) {
            log.info("[카카오검색] 키워드 0건 → 주소 검색 폴백: '{}'", regionQuery);
            allHits = fetchFromKakaoAddress(regionQuery);
        }

        int totalCount = allHits.size();

        if (fetchAll) {
            // 전체 반환 (프론트 캐싱용)
            Map<String, Object> result = new LinkedHashMap<>();
            result.put("hits", allHits);
            result.put("totalCount", totalCount);
            result.put("totalHits", totalCount);
            result.put("totalPages", 1);
            result.put("hitsPerPage", totalCount);
            result.put("isEnd", true);
            log.info("[카카오검색] 키워드 결과(전체): totalCount={}", totalCount);
            return ResponseEntity.ok(result);
        }

        // 슬라이싱 반환
        int size = Math.min(limit, 15);
        int fromIndex = Math.min(offset, totalCount);
        int toIndex = Math.min(offset + size, totalCount);
        List<Map<String, Object>> pageHits = allHits.subList(fromIndex, toIndex);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("hits", pageHits);
        result.put("totalCount", totalCount);
        result.put("totalHits", totalCount);
        result.put("totalPages", totalCount > 0 ? (int) Math.ceil((double) totalCount / size) : 0);
        result.put("hitsPerPage", size);
        result.put("isEnd", toIndex >= totalCount);

        log.info("[카카오검색] 키워드 결과: totalCount={}, hits={}", totalCount, pageHits.size());
        return ResponseEntity.ok(result);
    }

    /**
     * Kakao API를 is_end=true까지 반복 호출하여 전체 결과를 수집 (최대 45건)
     */
    @SuppressWarnings("unchecked")
    private List<Map<String, Object>> fetchAllFromKakao(String query) {
        List<Map<String, Object>> allHits = new ArrayList<>();
        int maxSize = 15; // Kakao API 최대 size
        int maxPage = 3;  // 15 * 3 = 45건 (Kakao 최대 pageable_count)

        for (int page = 1; page <= maxPage; page++) {
            String url = UriComponentsBuilder.fromHttpUrl(keywordApiUrl)
                    .queryParam("query", "{query}")
                    .queryParam("size", maxSize)
                    .queryParam("page", page)
                    .queryParam("rect", INCHEON_RECT)
                    .buildAndExpand(query)
                    .toUriString();

            try {
                ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.GET, authEntity(), Map.class);
                Map<String, Object> body = response.getBody();
                if (body == null) break;

                Map<String, Object> meta = (Map<String, Object>) body.get("meta");
                List<Map<String, Object>> documents = (List<Map<String, Object>>) body.get("documents");

                if (documents != null) {
                    for (Map<String, Object> doc : documents) {
                        allHits.add(toHit(doc));
                    }
                }

                // is_end이면 더 이상 조회할 필요 없음
                boolean isEnd = meta != null && Boolean.TRUE.equals(meta.get("is_end"));
                if (isEnd || documents == null || documents.isEmpty()) break;

            } catch (Exception e) {
                log.error("[카카오검색] 키워드 API 호출 실패 (page={}): {}", page, e.getMessage());
                break;
            }
        }

        return allHits;
    }

    /**
     * Kakao 주소 검색 API 호출 (키워드 0건 시 폴백)
     */
    @SuppressWarnings("unchecked")
    private List<Map<String, Object>> fetchFromKakaoAddress(String query) {
        List<Map<String, Object>> hits = new ArrayList<>();

        String url = UriComponentsBuilder.fromHttpUrl(addressApiUrl)
                .queryParam("query", "{query}")
                .queryParam("size", 15)
                .queryParam("page", 1)
                .buildAndExpand(query)
                .toUriString();

        try {
            ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.GET, authEntity(), Map.class);
            Map<String, Object> body = response.getBody();
            if (body == null) return hits;

            List<Map<String, Object>> documents = (List<Map<String, Object>>) body.get("documents");
            if (documents != null) {
                for (Map<String, Object> doc : documents) {
                    hits.add(toAddressHit(doc));
                }
            }
            log.info("[카카오검색] 주소 검색 결과: {}건", hits.size());
        } catch (Exception e) {
            log.error("[카카오검색] 주소 API 호출 실패: {}", e.getMessage());
        }

        return hits;
    }

    /** Kakao 주소 document → hit 변환 */
    @SuppressWarnings("unchecked")
    private Map<String, Object> toAddressHit(Map<String, Object> doc) {
        Map<String, Object> hit = new LinkedHashMap<>();
        String addressName = (String) doc.getOrDefault("address_name", "");
        hit.put("name", addressName);

        // 도로명 주소
        Object roadObj = doc.get("road_address");
        String roadAddr = "";
        if (roadObj instanceof Map) {
            roadAddr = (String) ((Map<String, Object>) roadObj).getOrDefault("address_name", "");
        }
        hit.put("road", roadAddr);

        // 지번 주소
        Object addrObj = doc.get("address");
        String jibunAddr = "";
        if (addrObj instanceof Map) {
            jibunAddr = (String) ((Map<String, Object>) addrObj).getOrDefault("address_name", "");
        }
        hit.put("jibun", jibunAddr);

        hit.put("id", "");
        hit.put("phone", "");
        hit.put("place_url", "");

        Map<String, Object> geo = new LinkedHashMap<>();
        geo.put("lng", doc.get("x"));
        geo.put("lat", doc.get("y"));
        hit.put("_geo", geo);

        hit.put("cat1", "주소");
        hit.put("cat2", "주소");
        hit.put("cat3", "");
        hit.put("cat4", "");

        return hit;
    }

    /** Kakao document → hit 변환 */
    @SuppressWarnings("unchecked")
    private Map<String, Object> toHit(Map<String, Object> doc) {
        Map<String, Object> hit = new LinkedHashMap<>();
        hit.put("name", doc.get("place_name"));
        hit.put("road", doc.getOrDefault("road_address_name", ""));
        hit.put("jibun", doc.getOrDefault("address_name", ""));
        hit.put("id", doc.get("id"));
        hit.put("phone", doc.getOrDefault("phone", ""));
        hit.put("place_url", doc.getOrDefault("place_url", ""));

        Map<String, Object> geo = new LinkedHashMap<>();
        geo.put("lng", doc.get("x"));
        geo.put("lat", doc.get("y"));
        hit.put("_geo", geo);

        String categoryName = (String) doc.getOrDefault("category_name", "");
        String[] cats = categoryName.split(" > ");
        hit.put("cat1", cats.length > 0 ? cats[0] : "-");
        hit.put("cat2", cats.length > 1 ? cats[1] : "-");
        hit.put("cat3", cats.length > 2 ? cats[2] : "-");
        hit.put("cat4", cats.length > 3 ? cats[3] : "-");

        return hit;
    }

    private HttpEntity<Void> authEntity() {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "KakaoAK " + kakaoApiKey);
        return new HttpEntity<>(headers);
    }

    private Map<String, Object> emptyResult() {
        Map<String, Object> empty = new LinkedHashMap<>();
        empty.put("hits", Collections.emptyList());
        empty.put("totalCount", 0);
        empty.put("totalHits", 0);
        empty.put("totalPages", 0);
        empty.put("hitsPerPage", 0);
        empty.put("isEnd", true);
        return empty;
    }
}
