package com.ga.api.service.accounting;

import com.ga.common.exception.BizException;
import com.ga.common.exception.ErrorCode;
import com.ga.common.model.PageResponse;
import com.ga.common.util.SecurityUtil;
import com.ga.core.mapper.accounting.AccountingCloseMapper;
import com.ga.core.mapper.accounting.ContractAccountingEntryMapper;
import com.ga.core.vo.accounting.AccountBalanceSnapshotVO;
import com.ga.core.vo.accounting.AccountingCloseResp;
import com.ga.core.vo.accounting.AccountingCloseSaveReq;
import com.ga.core.vo.accounting.AccountingCloseSearchParam;
import com.ga.core.vo.accounting.AccountingCloseVO;
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.util.List;

/**
 * 회계 결산 서비스 — DRAFT 행 생성, 마감(close), 재오픈, 잔액 스냅샷 조회.
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class AccountingCloseService {

    private final AccountingCloseMapper closeMapper;
    private final ContractAccountingEntryMapper entryMapper;

    public PageResponse<AccountingCloseResp> list(AccountingCloseSearchParam param) {
        param.startPage();
        return PageResponse.of(closeMapper.selectList(param));
    }

    public AccountingCloseResp detail(Long closeId) {
        AccountingCloseResp resp = closeMapper.selectDetailById(closeId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        return resp;
    }

    public List<AccountBalanceSnapshotVO> snapshots(Long closeId) {
        return closeMapper.selectSnapshotByCloseId(closeId);
    }

    /** DRAFT 행 생성. (closePeriod, closeType) 중복은 unique 제약으로 거부. */
    @Transactional
    public Long createDraft(AccountingCloseSaveReq req) {
        validateType(req.getCloseType());
        AccountingCloseVO vo = new AccountingCloseVO();
        vo.setClosePeriod(req.getClosePeriod());
        vo.setCloseType(req.getCloseType());
        vo.setCloseStatus("DRAFT");
        vo.setJournalPrefix(req.getClosePeriod());
        vo.setCreatedBy(SecurityUtil.getCurrentUserId());
        closeMapper.insert(vo);
        return vo.getCloseId();
    }

    /**
     * 마감 실행.
     *  1) 미전기 분개 ID 조회
     *  2) journal_no 채번 후 contract_accounting_entry.closeJournal
     *  3) 계정과목별 잔액 집계 → account_balance_snapshot INSERT
     *  4) accounting_close.updateClose
     */
    @Transactional
    public AccountingCloseResp close(Long closeId) {
        AccountingCloseResp head = closeMapper.selectDetailById(closeId);
        if (head == null) throw new BizException(ErrorCode.NOT_FOUND);
        if (!"DRAFT".equals(head.getCloseStatus()) && !"REOPENED".equals(head.getCloseStatus())) {
            throw new BizException(ErrorCode.INVALID_STATUS);
        }
        Long userId = SecurityUtil.getCurrentUserId();
        String journalPrefix = head.getJournalPrefix();

        List<Long> unposted = closeMapper.selectUnpostedEntryIds(head.getCloseType(), head.getClosePeriod());
        int journalCount = unposted.size();
        if (!unposted.isEmpty()) {
            String journalNo = journalPrefix + "-" + String.format("%04d", System.currentTimeMillis() % 10000);
            entryMapper.closeJournal(unposted, journalNo);
        }

        List<AccountBalanceSnapshotVO> agg = closeMapper.selectBalanceAggregate(journalPrefix);
        BigDecimal totalDebit  = BigDecimal.ZERO;
        BigDecimal totalCredit = BigDecimal.ZERO;
        for (AccountBalanceSnapshotVO s : agg) {
            s.setCloseId(closeId);
            if (s.getDebitTotal()  != null) totalDebit  = totalDebit.add(s.getDebitTotal());
            if (s.getCreditTotal() != null) totalCredit = totalCredit.add(s.getCreditTotal());
        }
        if (!agg.isEmpty()) {
            closeMapper.insertBalanceSnapshot(agg);
        }

        closeMapper.updateClose(closeId, journalCount, totalDebit, totalCredit, userId);
        log.info("[AccountingClose] closed closeId={} entries={} debit={} credit={}",
                closeId, journalCount, totalDebit, totalCredit);
        return closeMapper.selectDetailById(closeId);
    }

    /** 재오픈. 이미 마감된 결산만 가능. */
    @Transactional
    public void reopen(Long closeId, String reason) {
        if (reason == null || reason.isBlank()) throw new BizException(ErrorCode.INVALID_PARAMETER);
        int n = closeMapper.updateReopen(closeId, SecurityUtil.getCurrentUserId(), reason);
        if (n == 0) throw new BizException(ErrorCode.INVALID_STATUS);
    }

    private void validateType(String t) {
        if (!"MONTHLY".equals(t) && !"QUARTERLY".equals(t) && !"YEARLY".equals(t)) {
            throw new BizException(ErrorCode.INVALID_PARAMETER);
        }
    }
}
