package link.at17.mid.tushare.test;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import org.junit.Assert;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import link.at17.mid.tushare.dao.StockDailyDao;
import link.at17.mid.tushare.data.crawler.tushare.TushareCrawler;
import link.at17.mid.tushare.data.models.StockCalendar;
import link.at17.mid.tushare.data.service.StockCalendarService;
import link.at17.mid.tushare.enums.StockMarket;
import link.at17.mid.tushare.system.util.LocalDateTimeUtils;
import lombok.extern.slf4j.Slf4j;

@SpringBootTest
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class StockCalendarTest {

    @Autowired
    TushareCrawler tushareCrawler;
    
    @Autowired
    StockDailyDao stockDailyDao;

    @Autowired
    StockCalendarService stockCalendarService;
    
    /**
     * 测试开始
     */
    @Test
    @Order(1)
    void contextLoads() {
        log.info("测试前请确定数据库已经更新。本测试用例要求 stock_calendar 至少更新至 2021-8-16（含）");
    }
    
    /**
     * 测试数据更新
     */
    @Test
    @Order(2)
    void testUpdateStockCalendar() {
        boolean result = stockCalendarService.updateData();
        Assert.assertTrue("updateStockCalendar failed", result);
    }
    
    /**
     * 测试 getLatest
     */
    @Test
    @Order(3)
    void testQueryLatest() {
        // 测试不指定市场时的查询，应有查询结果且日期为本年度最后一天
        StockCalendar unassignedLatestCal = stockCalendarService.getLatest();
        Assert.assertNotNull(unassignedLatestCal);
        Assert.assertTrue(LocalDateTimeUtils.isLastDayOfThisYear(unassignedLatestCal.getDate()));

        // 测试深交所查询，应有查询结果，市场对应，且日期为本年度最后一天
        StockCalendar szLatestCal = stockCalendarService.getLatest(StockMarket.SZ);
        Assert.assertNotNull(szLatestCal);
        Assert.assertTrue(szLatestCal.getExchange().equals(StockMarket.SZ));
        Assert.assertTrue(LocalDateTimeUtils.isLastDayOfThisYear(szLatestCal.getDate()));

        // 测试上交所查询，应有查询结果，市场对应，且日期为本年度最后一天
        StockCalendar shLatestCal = stockCalendarService.getLatest(StockMarket.SH);
        Assert.assertNotNull(shLatestCal);
        Assert.assertTrue(shLatestCal.getExchange().equals(StockMarket.SH));
        Assert.assertTrue(LocalDateTimeUtils.isLastDayOfThisYear(shLatestCal.getDate()));

        // 测试非深交、上交所查询，应无查询结果
        StockCalendar notSzOrShLatestCal = stockCalendarService.getLatest(StockMarket.BJ);
        Assert.assertNull(notSzOrShLatestCal);
    }
    
    /**
     * 测试 tradeDateOffset
     */
    @Test
    @Order(4)
    void testTradeDateOffset() {
        /* 
         * 本测试用例情况
         * 2021-08-13 开市
         * 2021-08-14 闭市
         * 2021-08-15 闭市
         * 2021-08-16 开市
         * 
         */
        LocalDateTime d1 = LocalDateTime.of(2021, 8, 13, 0, 0);
        LocalDateTime d2 = LocalDateTime.of(2021, 8, 13, 15, 0);
        LocalDateTime d3 = LocalDateTime.of(2021, 8, 14, 0, 0);
        
        LocalDateTime p1d123 = LocalDateTime.of(2021, 8, 16, 0, 0);
        LocalDateTime d1p1 = stockCalendarService.getOpenDateOffset(StockMarket.SZ, d1,  1); // 20210816
        LocalDateTime d1o0 = stockCalendarService.getOpenDateOffset(StockMarket.SZ, d1,  0); // 20210813
        LocalDateTime d2p1 = stockCalendarService.getOpenDateOffset(StockMarket.SZ, d2,  1); // 20210816
        LocalDateTime d2o0 = stockCalendarService.getOpenDateOffset(StockMarket.SZ, d2,  0); // 20210813
        LocalDateTime d3p1 = stockCalendarService.getOpenDateOffset(StockMarket.SZ, d3,  1); // 20210816
        LocalDateTime d3n1 = stockCalendarService.getOpenDateOffset(StockMarket.SZ, d3, -1); // 20210813
        LocalDateTime d3o0 = stockCalendarService.getOpenDateOffset(StockMarket.SZ, d3,  0); // null

        log.info("d1: {}, offset  1: {}", d1, d1p1);
        log.info("d1: {}, offset  0: {}", d1, d1o0);
        log.info("d2: {}, offset  1: {}", d2, d2p1);
        log.info("d2: {}, offset  0: {}", d2, d2o0);
        log.info("d3: {}, offset  1: {}", d3, d3p1);
        log.info("d3: {}, offset -1: {}", d3, d3n1);
        log.info("d3: {}, offset  0: {}", d3, d3o0);

        Assert.assertTrue(LocalDateTimeUtils.isSameDay(d1p1, p1d123));
        Assert.assertTrue(LocalDateTimeUtils.isSameDay(d1o0, d1));
        Assert.assertTrue(LocalDateTimeUtils.isSameDay(d2p1, p1d123));
        Assert.assertTrue(LocalDateTimeUtils.isSameDay(d2o0, d2));
        Assert.assertTrue(LocalDateTimeUtils.isSameDay(d3p1, p1d123));
        Assert.assertTrue(LocalDateTimeUtils.isSameDay(d1, d3n1));
        Assert.assertNull(d3o0);

    }
    
    @Test
    @Order(5)
    void testCountOpenDaysBetween() {
        long between;
        LocalDateTime dt1 = LocalDateTime.of(2021, 8, 16,  1, 20, 30);
        LocalDateTime dt2 = LocalDateTime.of(2021, 8, 20, 19, 20, 30);
        between = stockCalendarService.countOpenDaysBetween(StockMarket.SH, dt1, dt2);
        log.info("between {} and {}: {}", dt1, dt2, between);
        Assert.assertTrue(between == 5);
        between = stockCalendarService.countOpenDaysBetween(StockMarket.SH, dt2, dt1);
        log.info("between {} and {}: {}", dt2, dt1, between);
        Assert.assertTrue(between == 5);

        LocalDate d1 = LocalDate.of(2021, 8, 16);
        LocalDate d2 = LocalDate.of(2021, 8, 20);
        
        between = stockCalendarService.countOpenDaysBetween(StockMarket.SH, d1, d2);
        log.info("between {} and {}: {}", d1, d2, between);
        Assert.assertTrue(between == 5);
        between = stockCalendarService.countOpenDaysBetween(StockMarket.SH, d2, d1);
        log.info("between {} and {}: {}", d2, d1, between);
        Assert.assertTrue(between == 5);

        between = stockCalendarService.countOpenDaysBetween(StockMarket.SH, d1, d1);
        log.info("between {} and {}: {}", d1, d1, between);
        Assert.assertTrue(between == 1);

        LocalDate d3 = LocalDate.of(2021, 8, 15);
        
        between = stockCalendarService.countOpenDaysBetween(StockMarket.SH, d3, d1);
        log.info("between {} and {}: {}", d3, d1, between);
        Assert.assertTrue(between == 1);
        between = stockCalendarService.countOpenDaysBetween(StockMarket.SH, d3, d3);
        log.info("between {} and {}: {}", d3, d3, between);
        Assert.assertTrue(between == 0);
        
    }
    
    

    @Test
    @Order(5)
    void testGetAllOpenDatesBetween() {
        LocalDateTime dt1 = LocalDateTime.of(2021, 8, 16,  1, 20, 30);
        LocalDateTime dt2 = LocalDateTime.of(2021, 8, 20, 19, 20, 30);
        List<LocalDateTime> betweens = stockCalendarService.getAllOpenDatesBetween(StockMarket.SH, dt1, dt2);
        
        List<LocalDateTime> assumed = List.of(
                LocalDateTime.of(2021, 8, 16, 0, 0, 0),
                LocalDateTime.of(2021, 8, 17, 0, 0, 0),
                LocalDateTime.of(2021, 8, 18, 0, 0, 0),
                LocalDateTime.of(2021, 8, 19, 0, 0, 0),
                LocalDateTime.of(2021, 8, 20, 0, 0, 0));
        
        Assert.assertArrayEquals(betweens.toArray(), assumed.toArray());
        
    }
    
}
