package com.cf.imes.module.system.service.funds.organamount;

import com.cf.imes.framework.common.enums.StatisticsUnit;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
import com.cf.imes.module.system.common.WithMockLoginUserExtension;
import com.cf.imes.module.system.controller.admin.funds.invoice.vo.InvoiceAmountRespVO;
import com.cf.imes.module.system.controller.admin.funds.organamount.vo.ManageAmountOverviewRespVO;
import com.cf.imes.module.system.controller.admin.funds.organamount.vo.OrganAmountGroupByCreateTimeRespVO;
import com.cf.imes.module.system.controller.admin.funds.organamount.vo.OrganFundsStatisticsReqVO;
import com.cf.imes.module.system.controller.admin.funds.organamount.vo.OrganProductOverviewRespVO;
import com.cf.imes.module.system.controller.admin.funds.organamount.vo.TotalSalesAmountRespVO;
import com.cf.imes.module.system.controller.admin.funds.purchase.vo.OrgEarliestPurchaseProductRespVO;
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
import com.cf.imes.module.system.dal.mysql.incomeandexpense.IncomeExpenseDetailsMapper;
import com.cf.imes.module.system.service.funds.invoice.InvoiceService;
import com.cf.imes.module.system.service.funds.purchase.PurchaseService;
import com.cf.imes.module.system.service.organ.OrganService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(WithMockLoginUserExtension.class)
class OrganAmountServiceImplTest extends BaseMockitoUnitTest {
    @Mock
    IncomeExpenseDetailsMapper incomeExpenseDetailsMapper;

    @InjectMocks
    private OrganAmountServiceImpl organAmountService;

    @Mock
    private OrganService organService;

    @Mock
    private PurchaseService purchaseService;

    @Mock
    private InvoiceService invoiceService;

    @Test
    @WithMockLoginUser(organId = 1L)
    void testGetOrganProductOverview_notInitialPurchase() {
        // ===== given =====
        Long organId = SecurityFrameworkUtils.getUserOrganId();

        PurchaseRecordDO recordDO = new PurchaseRecordDO();
        recordDO.setEndTime(LocalDate.of(2025, 12, 31));
        recordDO.setInitial(false);
        recordDO.setPurchaseDuration(12);
        recordDO.setPurchaseDurationUnit(1);

        OrgEarliestPurchaseProductRespVO resp = new OrgEarliestPurchaseProductRespVO(recordDO);

        when(purchaseService.getOrgEarliestProductDetail(organId))
                .thenReturn(resp);
        when(purchaseService.getInvoicingAmount(organId))
                .thenReturn(new BigDecimal("100.50"));

        // ===== when =====
        OrganProductOverviewRespVO result = organAmountService.getOrganProductOverview();

        // ===== then =====
        assertNotNull(result);

        assertEquals("2025-12-31 00:00:00", result.getProductEfficientDate());
        assertEquals(12, result.getDuration());
        assertEquals(1, result.getDurationUnit());
        assertEquals(new BigDecimal("100.50"), result.getInvoiceableAmount());

        verify(organService).validOrgan(organId);
        verify(purchaseService).getOrgEarliestProductDetail(organId);
        verify(purchaseService).getInvoicingAmount(organId);
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void testGetOrganProductOverview_noOrgEarliestPurchaseProduct() {
        // ===== given =====
        Long organId = SecurityFrameworkUtils.getUserOrganId();

        when(purchaseService.getOrgEarliestProductDetail(organId))
                .thenReturn(null);
        when(purchaseService.getInvoicingAmount(organId))
                .thenReturn(new BigDecimal("100.50"));

        // ===== when =====
        OrganProductOverviewRespVO result = organAmountService.getOrganProductOverview();

        // ===== then =====
        assertNotNull(result);

        assertNull(result.getProductEfficientDate());
        assertNull(result.getDuration());
        assertNull(result.getDurationUnit());
        assertEquals(new BigDecimal("100.50"), result.getInvoiceableAmount());

        verify(organService).validOrgan(organId);
        verify(purchaseService).getOrgEarliestProductDetail(organId);
        verify(purchaseService).getInvoicingAmount(organId);
    }

    @Test
    @WithMockLoginUser(organId = 1L)
    void testGetOrganProductOverview_initialPurchase() {
        Long organId = SecurityFrameworkUtils.getUserOrganId();

        PurchaseRecordDO recordDO = new PurchaseRecordDO();
        recordDO.setEndTime(LocalDate.of(2025, 1, 1));
        recordDO.setInitial(true);

        OrgEarliestPurchaseProductRespVO resp = new OrgEarliestPurchaseProductRespVO(recordDO);

        when(purchaseService.getOrgEarliestProductDetail(organId))
                .thenReturn(resp);
        when(purchaseService.getInvoicingAmount(organId))
                .thenReturn(BigDecimal.ZERO);

        OrganProductOverviewRespVO result = organAmountService.getOrganProductOverview();

        assertNotNull(result);
        assertNull(result.getDuration());
        assertNull(result.getDurationUnit());
    }

    @Test
    void testGetOrganAmountAll() {
        // given
        TotalSalesAmountRespVO totalSales = new TotalSalesAmountRespVO(new BigDecimal("9999.99"));

        when(purchaseService.getTotalSales()).thenReturn(totalSales);

        InvoiceAmountRespVO invoiceAmount = new InvoiceAmountRespVO();
        invoiceAmount.setPendingInvoice(new BigDecimal("123.45"));

        when(invoiceService.getManageInvoiceAmount())
                .thenReturn(invoiceAmount);

        // when
        ManageAmountOverviewRespVO result = organAmountService.getOrganAmountAll();

        // then
        assertNotNull(result);
        assertEquals(new BigDecimal("9999.99"), result.getTotalSalesAmount());
        assertEquals(new BigDecimal("123.45"), result.getToExamineAmount());

        verify(purchaseService).getTotalSales();
        verify(invoiceService).getManageInvoiceAmount();
    }


    @Test
    @WithMockLoginUser(organId = 1L)
    void should_return_consumption_trend_with_zero_filling() {
        Long organId = SecurityFrameworkUtils.getUserOrganId();

        OrganFundsStatisticsReqVO reqVO = new OrganFundsStatisticsReqVO();
        reqVO.setUnit(StatisticsUnit.DAY.getValue());
        reqVO.setCreateTime(new LocalDate[]{
                LocalDate.now().minusDays(2),
                LocalDate.now().plusDays(1)
        });

        LocalDate now = LocalDate.now();

        OrganAmountGroupByCreateTimeRespVO vo1 = new OrganAmountGroupByCreateTimeRespVO();
        vo1.setDate(now.minusDays(2).format(DateTimeFormatter.ofPattern("yyyy-M-d")));
        vo1.setAmount(new BigDecimal("100"));

        OrganAmountGroupByCreateTimeRespVO vo2 = new OrganAmountGroupByCreateTimeRespVO();
        vo2.setDate(now.format(DateTimeFormatter.ofPattern("yyyy-M-d")));
        vo2.setAmount(new BigDecimal("200"));

        OrganAmountGroupByCreateTimeRespVO vo3 = new OrganAmountGroupByCreateTimeRespVO();
        vo3.setDate(now.minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-M-d")));
        vo3.setAmount(new BigDecimal("200"));

        List<OrganAmountGroupByCreateTimeRespVO> organAmountGroupByCreateTimeRespVOS = new ArrayList<>();
        organAmountGroupByCreateTimeRespVOS.add(vo1);
        organAmountGroupByCreateTimeRespVOS.add(vo2);

        when(incomeExpenseDetailsMapper.selectOrganIncomeExpenseTrendGroupByCreateTime(reqVO, organId)).thenReturn(organAmountGroupByCreateTimeRespVOS);

        // when query org
        Map<String, Object> result =
                organAmountService.getConsumptionTrend(reqVO, true);

        // then
        List<?> xAxis = (List<?>) result.get("dateList");
        List<?> series = (List<?>) result.get("series");

        assertEquals(4, xAxis.size());

        Map<?, ?> seriesItem = (Map<?, ?>) series.get(0);
        BigDecimal[] data = (BigDecimal[]) seriesItem.get("data");

        assertArrayEquals(
                new BigDecimal[]{
                        new BigDecimal("100"),
                        BigDecimal.ZERO,
                        new BigDecimal("200"),
                        BigDecimal.ZERO,
                },
                data
        );

        organAmountGroupByCreateTimeRespVOS.add(vo3);
        when(incomeExpenseDetailsMapper.selectOrganIncomeExpenseTrendGroupByCreateTime(reqVO, null)).thenReturn(organAmountGroupByCreateTimeRespVOS);
        // when not query org
        result = organAmountService.getConsumptionTrend(reqVO, false);

        // then
        xAxis = (List<?>) result.get("dateList");
        series = (List<?>) result.get("series");

        assertEquals(4, xAxis.size());

        seriesItem = (Map<?, ?>) series.get(0);
        data = (BigDecimal[]) seriesItem.get("data");

        assertArrayEquals(
                new BigDecimal[]{
                        new BigDecimal("100"),
                        new BigDecimal("200"),
                        new BigDecimal("200"),
                        BigDecimal.ZERO,
                },
                data
        );
    }
}
