package com.zioinfo.kintex.auction;

import com.zioinfo.kintex.auction.dto.AuctionDtos.BoqLine;
import com.zioinfo.kintex.auction.dto.AuctionDtos.BoqResult;
import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * F035 물량서(BOQ) 자동산출 서비스 — 옥션 자료 패키지 첨부용.
 *
 * <p>부스 설계 데이터(M2 booth/layout·M4 utility_order — <b>읽기 전용</b>)에서 공사 물량을
 * 규칙 기반으로 집계한다({@link BoqCalc}). 옥션 제목에서 부스코드를 추출해 해당 부스로 스코프를
 * 좁히고, 매칭 부스가 없으면 행사 전체 부스로 집계한다. 설계 데이터가 없으면 안내 note 를 반환한다.
 */
@Service
public class BoqService {

    private static final Pattern BOOTH_CODE = Pattern.compile("([A-Za-z]-?\\d{1,4})");

    private final AuctionMapper mapper;

    public BoqService(AuctionMapper mapper) {
        this.mapper = mapper;
    }

    public BoqResult boq(String auctionId) {
        Map<String, Object> a = mapper.findAuction(auctionId);
        if (a == null) {
            throw new ApiException(ErrorCode.NOT_FOUND);
        }
        String eventId = str(a.get("eventId"));
        String boothCode = boothCode(str(a.get("title")));

        // 1) 제목의 부스코드로 단일 부스 시도 → 없으면 행사 전체 집계로 폴백.
        String scope = "booth";
        String scopeLabel = boothCode;
        Map<String, Object> agg = boothCode == null ? null : mapper.boqBoothAggregate(eventId, boothCode);
        int boothCount = agg == null ? 0 : intOr(agg.get("boothCount"), 0);
        if (boothCount == 0) {
            agg = mapper.boqBoothAggregate(eventId, null);
            boothCount = agg == null ? 0 : intOr(agg.get("boothCount"), 0);
            scope = "event";
            scopeLabel = "행사 전체";
        }

        double areaM2 = agg == null ? 0 : dbl(agg.get("areaM2"));
        double perimeterM = agg == null ? 0 : dbl(agg.get("perimeterM"));
        double heightM = agg == null ? 0 : dbl(agg.get("maxHeightM"));

        String boothNoFilter = "booth".equals(scope) ? boothCode : null;
        Map<String, Object> util = mapper.boqUtilityAggregate(eventId, boothNoFilter);
        int utilityCount = util == null ? 0 : intOr(util.get("orderCount"), 0);

        List<BoqLine> lines = BoqCalc.compute(areaM2, perimeterM, boothCount, heightM, utilityCount);
        String note = lines.isEmpty()
                ? "배치·설계 데이터가 아직 확정되지 않아 물량이 산출되지 않았습니다. "
                + "배치도(M2)·설계안(M3)·유틸리티(M4) 확정 후 자동 산출됩니다."
                : "규칙 기반 자동 산출값입니다. 단가·금액은 응찰 견적서에서 산정됩니다.";

        return new BoqResult(auctionId, eventId, scope, scopeLabel,
                boothCount, Math.round(areaM2), lines, note, Instant.now().toString());
    }

    /** 제목에서 부스코드 추출(예: "A-102 독립부스 시공" → "A-102"). 없으면 null → 행사 전체 스코프. */
    private static String boothCode(String title) {
        if (title == null || title.isBlank()) {
            return null;
        }
        Matcher m = BOOTH_CODE.matcher(title);
        return m.find() ? m.group(1) : null;
    }

    private static String str(Object o) {
        return o == null ? null : String.valueOf(o);
    }

    private static int intOr(Object o, int def) {
        if (o == null) return def;
        if (o instanceof Number n) return n.intValue();
        try {
            return Integer.parseInt(String.valueOf(o));
        } catch (NumberFormatException e) {
            return def;
        }
    }

    private static double dbl(Object o) {
        if (o == null) return 0.0;
        if (o instanceof Number n) return n.doubleValue();
        try {
            return Double.parseDouble(String.valueOf(o));
        } catch (NumberFormatException e) {
            return 0.0;
        }
    }
}
