package com.erp.service;

import com.erp.common.BaseService;
import com.erp.util.DecimalUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class TaxInvoiceService extends BaseService {

    private static final String NS = "taxInvoice.";
    private final CommonService commonService;

    public Map<String, Object> getTaxInvoiceList(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        commonService.applyPagination(params);
        int totalCount = sqlSession.selectOne(NS + "getTaxInvoiceListCnt", params);
        List<Map<String, Object>> list = sqlSession.selectList(NS + "getTaxInvoiceList", params);
        return commonService.buildListResponse(list, totalCount, params);
    }

    public Map<String, Object> getTaxInvoiceInfo(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        Map<String, Object> invoice = sqlSession.selectOne(NS + "getTaxInvoiceInfo", params);
        if (invoice == null) return null;
        List<Map<String, Object>> items = sqlSession.selectList(NS + "getTaxInvoiceItems", params);
        Map<String, Object> result = new HashMap<>();
        result.put("invoice", invoice);
        result.put("items", items);
        return result;
    }

    private String generateInvoiceNumber(String companyCode) {
        LocalDateTime now = LocalDateTime.now();
        String yearMonth = String.format("%04d%02d", now.getYear(), now.getMonthValue());
        String prefix = yearMonth + "-";
        Map<String, Object> params = new HashMap<>();
        params.put("company_code", companyCode);
        params.put("prefix", prefix + "%");
        String lastNum = sqlSession.selectOne(NS + "getLastInvoiceNumber", params);
        int nextNum = 1;
        if (lastNum != null && !lastNum.isEmpty()) {
            String[] parts = lastNum.split("-");
            if (parts.length == 2) {
                try {
                    nextNum = Integer.parseInt(parts[1]) + 1;
                } catch (NumberFormatException e) {
                    nextNum = 1;
                }
            }
        }
        return String.format("%s%05d", prefix, nextNum);
    }

    @Transactional
    public Map<String, Object> insertTaxInvoice(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        params.put("supply_amount", DecimalUtils.toBigDecimal(params.get("supply_amount")));
        params.put("tax_amount", DecimalUtils.toBigDecimal(params.get("tax_amount")));
        params.put("total_amount", DecimalUtils.toBigDecimal(params.get("total_amount")));
        String invoiceNumber = generateInvoiceNumber((String) params.get("company_code"));
        params.put("invoice_number", invoiceNumber);
        sqlSession.insert(NS + "insertTaxInvoice", params);
        Object itemsObj = params.get("items");
        if (itemsObj instanceof List<?> itemsList) {
            for (int i = 0; i < itemsList.size(); i++) {
                @SuppressWarnings("unchecked")
                Map<String, Object> item = (Map<String, Object>) itemsList.get(i);
                item.put("tax_invoice_id", params.get("id"));
                item.put("company_code", params.get("company_code"));
                item.put("item_seq", i + 1);
                item.put("supply_amount", DecimalUtils.toBigDecimal(item.get("supply_amount")));
                item.put("tax_amount", DecimalUtils.toBigDecimal(item.get("tax_amount")));
                item.put("unit_price", DecimalUtils.toBigDecimal(item.get("unit_price")));
                sqlSession.insert(NS + "insertTaxInvoiceItem", item);
            }
        }
        return params;
    }

    @Transactional
    public Map<String, Object> updateTaxInvoice(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        Map<String, Object> existing = sqlSession.selectOne(NS + "getTaxInvoiceInfo", params);
        if (existing == null) {
            throw new RuntimeException("세금계산서를 찾을 수 없습니다.");
        }
        if (!"draft".equals(existing.get("invoice_status"))) {
            throw new RuntimeException("발행된 세금계산서는 수정할 수 없습니다.");
        }
        if (params.get("supply_amount") != null) params.put("supply_amount", DecimalUtils.toBigDecimal(params.get("supply_amount")));
        if (params.get("tax_amount") != null) params.put("tax_amount", DecimalUtils.toBigDecimal(params.get("tax_amount")));
        if (params.get("total_amount") != null) params.put("total_amount", DecimalUtils.toBigDecimal(params.get("total_amount")));
        sqlSession.update(NS + "updateTaxInvoice", params);
        Object itemsObj = params.get("items");
        if (itemsObj != null) {
            sqlSession.delete(NS + "deleteTaxInvoiceItemsByInvoiceId", params);
            if (itemsObj instanceof List<?> itemsList) {
                for (int i = 0; i < itemsList.size(); i++) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> item = (Map<String, Object>) itemsList.get(i);
                    item.put("tax_invoice_id", params.get("id"));
                    item.put("company_code", params.get("company_code"));
                    item.put("item_seq", i + 1);
                    item.put("supply_amount", DecimalUtils.toBigDecimal(item.get("supply_amount")));
                    item.put("tax_amount", DecimalUtils.toBigDecimal(item.get("tax_amount")));
                    item.put("unit_price", DecimalUtils.toBigDecimal(item.get("unit_price")));
                    sqlSession.insert(NS + "insertTaxInvoiceItem", item);
                }
            }
        }
        return params;
    }

    @Transactional
    public Map<String, Object> deleteTaxInvoice(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        Map<String, Object> existing = sqlSession.selectOne(NS + "getTaxInvoiceInfo", params);
        if (existing == null) {
            throw new RuntimeException("세금계산서를 찾을 수 없습니다.");
        }
        if (!"draft".equals(existing.get("invoice_status"))) {
            throw new RuntimeException("발행된 세금계산서는 삭제할 수 없습니다.");
        }
        sqlSession.delete(NS + "deleteTaxInvoiceItemsByInvoiceId", params);
        sqlSession.delete(NS + "deleteTaxInvoice", params);
        return params;
    }

    @Transactional
    public Map<String, Object> issueTaxInvoice(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        int updated = sqlSession.update(NS + "issueTaxInvoice", params);
        if (updated == 0) {
            throw new RuntimeException("세금계산서를 찾을 수 없거나 이미 발행된 상태입니다.");
        }
        return sqlSession.selectOne(NS + "getTaxInvoiceInfo", params);
    }

    @Transactional
    public Map<String, Object> cancelTaxInvoice(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        int updated = sqlSession.update(NS + "cancelTaxInvoice", params);
        if (updated == 0) {
            throw new RuntimeException("세금계산서를 찾을 수 없거나 취소할 수 없는 상태입니다.");
        }
        return sqlSession.selectOne(NS + "getTaxInvoiceInfo", params);
    }

    public Map<String, Object> getMonthlyStats(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        int year = params.get("year") != null ? Integer.parseInt(params.get("year").toString()) : LocalDateTime.now().getYear();
        int month = params.get("month") != null ? Integer.parseInt(params.get("month").toString()) : LocalDateTime.now().getMonthValue();
        String startDate = String.format("%04d-%02d-01", year, month);
        LocalDate firstDay = LocalDate.of(year, month, 1);
        String endDate = firstDay.withDayOfMonth(firstDay.lengthOfMonth()).toString();
        params.put("start_date", startDate);
        params.put("end_date", endDate);
        List<Map<String, Object>> rows = sqlSession.selectList(NS + "getMonthlyStats", params);
        Map<String, Object> sales = new HashMap<>();
        sales.put("count", 0);
        sales.put("supply_amount", BigDecimal.ZERO);
        sales.put("tax_amount", BigDecimal.ZERO);
        sales.put("total_amount", BigDecimal.ZERO);
        Map<String, Object> purchase = new HashMap<>();
        purchase.put("count", 0);
        purchase.put("supply_amount", BigDecimal.ZERO);
        purchase.put("tax_amount", BigDecimal.ZERO);
        purchase.put("total_amount", BigDecimal.ZERO);
        for (Map<String, Object> row : rows) {
            String type = (String) row.get("invoice_type");
            Map<String, Object> target = "sales".equals(type) ? sales : purchase;
            target.put("count", row.get("count"));
            target.put("supply_amount", DecimalUtils.toBigDecimal(row.get("supply_amount")));
            target.put("tax_amount", DecimalUtils.toBigDecimal(row.get("tax_amount")));
            target.put("total_amount", DecimalUtils.toBigDecimal(row.get("total_amount")));
        }
        Map<String, Object> result = new HashMap<>();
        result.put("sales", sales);
        result.put("purchase", purchase);
        result.put("year", year);
        result.put("month", month);
        return result;
    }

    public Map<String, Object> getCostTypeStats(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        Integer yearInt = params.get("year") != null ? Integer.parseInt(params.get("year").toString()) : null;
        Integer monthInt = params.get("month") != null ? Integer.parseInt(params.get("month").toString()) : null;
        params.put("year", yearInt);
        params.put("month", monthInt);
        if (yearInt != null && monthInt != null) {
            String startDate = String.format("%04d-%02d-01", yearInt, monthInt);
            LocalDate firstDay = LocalDate.of(yearInt, monthInt, 1);
            String endDate = firstDay.withDayOfMonth(firstDay.lengthOfMonth()).toString();
            params.put("start_date", startDate);
            params.put("end_date", endDate);
        }
        List<Map<String, Object>> byCostType = sqlSession.selectList(NS + "getCostTypeStats", params);
        List<Map<String, Object>> byMonth = sqlSession.selectList(NS + "getCostTypeStatsByMonth", params);
        Map<String, Object> summary = sqlSession.selectOne(NS + "getCostTypeStatsSummary", params);
        for (Map<String, Object> row : byCostType) {
            row.put("supply_amount", DecimalUtils.toBigDecimal(row.get("supply_amount")));
            row.put("tax_amount", DecimalUtils.toBigDecimal(row.get("tax_amount")));
            row.put("total_amount", DecimalUtils.toBigDecimal(row.get("total_amount")));
        }
        for (Map<String, Object> row : byMonth) {
            row.put("total_amount", DecimalUtils.toBigDecimal(row.get("total_amount")));
        }
        if (summary != null) {
            summary.put("total_amount", DecimalUtils.toBigDecimal(summary.get("total_amount")));
            summary.put("purchase_amount", DecimalUtils.toBigDecimal(summary.get("purchase_amount")));
            summary.put("installation_amount", DecimalUtils.toBigDecimal(summary.get("installation_amount")));
            summary.put("repair_amount", DecimalUtils.toBigDecimal(summary.get("repair_amount")));
            summary.put("maintenance_amount", DecimalUtils.toBigDecimal(summary.get("maintenance_amount")));
            summary.put("disposal_amount", DecimalUtils.toBigDecimal(summary.get("disposal_amount")));
            summary.put("other_amount", DecimalUtils.toBigDecimal(summary.get("other_amount")));
        }
        Map<String, Object> result = new HashMap<>();
        result.put("by_cost_type", byCostType);
        result.put("by_month", byMonth);
        result.put("summary", summary);
        return result;
    }
}
