import { Injectable } from '@nestjs/common';
import dayjs from 'dayjs';
import { MemberType } from '../fc/fc.entity';
import { GetStatisticsListDto } from './statistics.dto';
import { StatisticsRepository } from './statistics.repository';
import axios from 'axios';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class StatisticsService {
  private googleChatWebHookUrl?: string;

  constructor(
    private statisticsRepository: StatisticsRepository,
    private configService: ConfigService,
  ) {
    this.googleChatWebHookUrl = this.configService.get(
      'chatbot.googleChatWebhook',
    );
  }

  async getStatistics(dto: GetStatisticsListDto) {
    const startDate = dto.yearMonth
      ? dayjs(dto.yearMonth).startOf('month').format('YYYY-MM-DD HH:mm:ss')
      : dayjs().startOf('month').format('YYYY-MM-DD HH:mm:ss');

    const endDate = dto.yearMonth
      ? dayjs(dto.yearMonth).endOf('month').format('YYYY-MM-DD HH:mm:ss')
      : dayjs().endOf('month').format('YYYY-MM-DD HH:mm:ss');

    // 전체회원수
    const allMemberQty = await this.statisticsRepository.getAllMemberQty();

    // 월신규회원수
    const memberQty = await this.statisticsRepository.getNewMemberQty(
      startDate,
      endDate,
    );

    // 전체분석완료건수
    const allUsedQty =
      await this.statisticsRepository.getAllUsedOrderDetailQty();

    // 월분석완료건수
    const usedQty = await this.statisticsRepository.getUsedOrderDetailQty(
      startDate,
      endDate,
    );

    // 전체 결제/결제취소 금액
    const allPay = await this.statisticsRepository.getAllPay('pay');
    const allPayCancel = await this.statisticsRepository.getAllPay('cancel');

    // 월별 결제/결제취소 금액
    const payMonth = await this.statisticsRepository.getPayMonth(
      'pay',
      startDate,
      endDate,
    );
    const payCancelMonth = await this.statisticsRepository.getPayMonth(
      'cancel',
      startDate,
      endDate,
    );

    const allDates = this.generateDayRange(startDate, endDate);

    // 일별결제/결제취소 금액
    const payList = await this.statisticsRepository.getPayDaily(
      'pay',
      startDate,
      endDate,
    );
    const payCancelList = await this.statisticsRepository.getPayDaily(
      'cancel',
      startDate,
      endDate,
    );

    return {
      allMemberQty: allMemberQty.count,
      memberQty: memberQty.count,
      usedQty: usedQty.count,
      allUsedQty: allUsedQty.count,
      payMonth,
      payCancelMonth,
      payList: this.buildPayDailys(payList, allDates),
      payCancelList: this.buildPayDailys(payCancelList, allDates),
      allPay,
      allPayCancel,
    };
  }

  //* 영업대리점 통계
  async getSalesStatistics(dto: GetStatisticsListDto, sagId: number) {
    const startDate = dto.yearMonth
      ? dayjs(dto.yearMonth).startOf('month').format('YYYY-MM-DD HH:mm:ss')
      : dayjs().startOf('month').format('YYYY-MM-DD HH:mm:ss');

    const endDate = dto.yearMonth
      ? dayjs(dto.yearMonth).endOf('month').format('YYYY-MM-DD HH:mm:ss')
      : dayjs().endOf('month').format('YYYY-MM-DD HH:mm:ss');

    // 전체회원수
    const allMemberQty =
      await this.statisticsRepository.getSalesAllMemberQty(sagId);

    // 월신규회원수
    const memberQty = await this.statisticsRepository.getSalesNewMemberQty(
      startDate,
      endDate,
      sagId,
    );

    // 전체분석완료건수
    const allUsedQty =
      await this.statisticsRepository.getSalesAllUsedOrderDetailQty(sagId);

    // 월분석완료건수
    const usedQty = await this.statisticsRepository.getSalesUsedOrderDetailQty(
      startDate,
      endDate,
      sagId,
    );

    // 전체 결제/결제취소 금액
    const allPay = await this.statisticsRepository.getSalesAllPay('pay', sagId);
    const allPayCancel = await this.statisticsRepository.getSalesAllPay(
      'cancel',
      sagId,
    );

    // 월별 결제/결제취소 금액
    const payMonth = await this.statisticsRepository.getSalesPayMonth(
      'pay',
      startDate,
      endDate,
      sagId,
    );
    const payCancelMonth = await this.statisticsRepository.getSalesPayMonth(
      'cancel',
      startDate,
      endDate,
      sagId,
    );

    const allDates = this.generateDayRange(startDate, endDate);

    // 일별결제/결제취소 금액
    const payList = await this.statisticsRepository.getSalesPayDaily(
      'pay',
      startDate,
      endDate,
      sagId,
    );
    const payCancelList = await this.statisticsRepository.getSalesPayDaily(
      'cancel',
      startDate,
      endDate,
      sagId,
    );

    return {
      allMemberQty: allMemberQty.count,
      memberQty: memberQty.count,
      usedQty: usedQty.count,
      allUsedQty: allUsedQty.count,
      payMonth,
      payCancelMonth,
      payList: this.buildPayDailys(payList, allDates),
      payCancelList: this.buildPayDailys(payCancelList, allDates),
      allPay,
      allPayCancel,
    };
  }

  //* 가입통계 조회
  async getSignUpStatistics(
    dateType: 'month' | 'day',
    date: string,
    memType: MemberType,
  ) {
    const { startDate, endDate } = this.formatDateRange(dateType, date);

    // 기간 내 가입통계 조회
    const result = await this.statisticsRepository.getSignUpStatistics(
      dateType,
      startDate,
      endDate,
      memType,
    );

    // 기간 내 가입/탈퇴 결과
    const allDates =
      dateType === 'month'
        ? this.generateMonthRange(startDate, endDate)
        : this.generateDayRange(startDate, endDate);

    const list = this.buildSignUpStatisticsDetails(dateType, result, allDates);

    // 전월/전일 비율 계산
    const listWithRate = this.calculateSignupStatisticsRate(list);

    return {
      statisticsList: listWithRate,
    };
  }

  //* 매출통계 조회
  async getRevenueStatistics(
    dateType: 'month' | 'day',
    date: string,
    memType: MemberType,
  ) {
    const { startDate, endDate } = this.formatDateRange(dateType, date);

    // 기간 내 매출 조회
    const result = await this.statisticsRepository.getRevenueStatistics(
      dateType,
      startDate,
      endDate,
      memType,
    );

    // 기간 내 매출결과
    const allDates =
      dateType === 'month'
        ? this.generateMonthRange(startDate, endDate)
        : this.generateDayRange(startDate, endDate);

    const list = this.buildRevenueStatisticsDetails(result, allDates);

    return {
      statisticsList: list,
    };
  }

  //* 분석통계 조회
  async getAnalysisStatistics(dateType: 'month' | 'day', date?: string) {
    const { startDate, endDate } = this.formatDateRange(dateType, date);

    //* 기간 내 분석결과 조회
    const statistics = await this.statisticsRepository.getAnalysisStatistics(
      dateType,
      startDate,
      endDate,
    );

    //* 분석결과별 통계
    const analysisCount = this.calculateAnalysisStatisticsCount(statistics);

    //* 분석완료/분석실패
    const analysisOverallRate = {
      successRate: this.calculateRate(
        analysisCount.successTotal,
        analysisCount.total,
      ),
      failureRate: this.calculateRate(
        analysisCount.failureTotal,
        analysisCount.total,
      ),
    };

    //* 인증실패/분석실패/기타
    const analysisFailureRate = {
      authFailureRate: this.calculateRate(
        analysisCount.authFailureTotal,
        analysisCount.failureTotal,
      ),
      analysisFailureRate: this.calculateRate(
        analysisCount.analysisFailureTotal,
        analysisCount.failureTotal,
      ),
      etcRate: this.calculateRate(
        analysisCount.etcTotal,
        analysisCount.failureTotal,
      ),
    };

    //* 기간 내 분석결과 상세
    const allDates =
      dateType === 'month'
        ? this.generateMonthRange(startDate, endDate)
        : this.generateDayRange(startDate, endDate);

    // 상세리스트 생성
    const list = this.buildAnalysisStatisticsDetails(statistics, allDates);

    return {
      analysisOverallRate,
      analysisFailureRate,
      statisticsList: list,
    };
  }

  // //* 챗봇 통계 조회
  async getStatisticsForChatbot(date: string) {
    //TODO: 가입통계/매출통계 작업 완료 시 코드 수정 및 리팩토링 진행
    const monthDate = {
      startDate: dayjs(date).startOf('month').format('YYYY-MM-DD HH:mm:ss'),
      endDate: dayjs(date).endOf('month').format('YYYY-MM-DD HH:mm:ss'),
    };
    const dayDate = {
      startDate: dayjs(date).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
      endDate: dayjs(date).endOf('day').format('YYYY-MM-DD HH:mm:ss'),
    };

    // 월별/일별 데이터 불러오기
    const monthStatistics =
      await this.statisticsRepository.getStatisticsForChatbot(
        'month',
        monthDate.startDate,
        monthDate.endDate,
      );
    const dayStatistics =
      await this.statisticsRepository.getStatisticsForChatbot(
        'day',
        dayDate.startDate,
        dayDate.endDate,
      );

    //* 가입통계
    const monthSignupStatistics = this.signupDateDivision(
      monthStatistics.signupStatistics,
      date,
      'month',
    );

    const daySignupStatistics = this.signupDateDivision(
      dayStatistics.signupStatistics,
      date,
      'day',
    );

    const totalAllSignCount =
      await this.statisticsRepository.getSignAllTotalCount();

    const signupStatistics = {
      month: {
        signCount: monthSignupStatistics.result[0].signupCount,
        signRate: this.signCalculateRate(
          monthSignupStatistics.result[0].signupCount,
          monthSignupStatistics.beforeResult[0].signupCount,
        ),
        resignCount: monthSignupStatistics.result[0].resignCount,
        resignRate: this.signCalculateRate(
          monthSignupStatistics.result[0].resignCount,
          monthSignupStatistics.beforeResult[0].resignCount,
        ),
      },
      day: {
        signCount: daySignupStatistics.result[0].signupCount,
        signRate: this.signCalculateRate(
          daySignupStatistics.result[0].signupCount,
          daySignupStatistics.beforeResult[0].signupCount,
        ),
        resignCount: daySignupStatistics.result[0].resignCount,
        resignRate: this.signCalculateRate(
          daySignupStatistics.result[0].resignCount,
          daySignupStatistics.beforeResult[0].resignCount,
        ),
      },
      total: totalAllSignCount,
    };

    //* 매출통계
    const monthRevenue =
      monthStatistics.revenueStatistics.length > 0
        ? monthStatistics.revenueStatistics
        : [{ date: date, payAmount: '0', cancelAmount: '0' }];

    const dayRevenue =
      dayStatistics.revenueStatistics.length > 0
        ? dayStatistics.revenueStatistics
        : [{ date: date, payAmount: '0', cancelAmount: '0' }];

    //* 분석통계
    const monthAnalysisStatistics =
      monthStatistics.analysisStatistics.length > 0
        ? monthStatistics.analysisStatistics[0]
        : {
            total: 0,
            failedTotal: 0,
            analysisFailed: 0,
            authFailed: 0,
            etc: 0,
          };

    const dayAnalysisStatistics =
      dayStatistics.analysisStatistics.length > 0
        ? dayStatistics.analysisStatistics[0]
        : {
            total: 0,
            failedTotal: 0,
            analysisFailed: 0,
            authFailed: 0,
            etc: 0,
          };

    const analysisStatistics = {
      month: {
        totalCount: monthAnalysisStatistics.total,
        failedCount: monthAnalysisStatistics.failedTotal,
        analysisFailedCount: monthAnalysisStatistics.analysisFailed,
        authFailedCount: monthAnalysisStatistics.authFailed,
        etcCount: monthAnalysisStatistics.etc,
      },
      day: {
        totalCount: dayAnalysisStatistics.total,
        failedCount: dayAnalysisStatistics.failedTotal,
        analysisFailedCount: dayAnalysisStatistics.analysisFailed,
        authFailedCount: dayAnalysisStatistics.authFailed,
        etcCount: dayAnalysisStatistics.etc,
      },
    };

    const payload = {
      cardsV2: [
        {
          cardId: 'daily-stats',
          card: {
            header: {
              title: '오늘의 종합 통계',
              subtitle: new Date().toLocaleDateString('ko-KR'),
            },
            sections: [
              {
                header: '가입통계',
                widgets: [
                  {
                    columns: {
                      columnItems: [
                        {
                          horizontalSizeStyle: 'FILL_AVAILABLE_SPACE',
                          widgets: [
                            {
                              decoratedText: {
                                topLabel: '당일 가입자 수',
                                text:
                                  signupStatistics.day.signCount.toString() +
                                  '명',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '당일 탈퇴자 수',
                                text:
                                  signupStatistics.day.resignCount.toString() +
                                  '명',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '당월 가입자자 수',
                                text:
                                  signupStatistics.month.signCount.toString() +
                                  '명',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '당월 탈퇴자 수',
                                text:
                                  signupStatistics.month.resignCount.toString() +
                                  '명',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '전체 가입자 수',
                                text:
                                  signupStatistics.total.totalAllSignUp.toString() +
                                  '명',
                              },
                            },
                          ],
                        },
                        {
                          horizontalSizeStyle: 'FILL_AVAILABLE_SPACE',
                          widgets: [
                            {
                              decoratedText: {
                                topLabel: '전일 대비 가입률',
                                text:
                                  signupStatistics.day.signRate.toString() +
                                  '%',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '전일 대비 탈퇴율',
                                text:
                                  signupStatistics.day.resignRate.toString() +
                                  '%',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '전월 대비 가입율',
                                text:
                                  signupStatistics.month.signRate.toString() +
                                  '%',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '전월월 대비 탈퇴율',
                                text:
                                  signupStatistics.month.resignRate.toString() +
                                  '%',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '전체 탈퇴자 수',
                                text:
                                  signupStatistics.total.totalAllResign.toString() +
                                  '명',
                              },
                            },
                          ],
                        },
                      ],
                    },
                  },
                ],
              },
              {
                header: '매출통계',
                widgets: [
                  {
                    columns: {
                      columnItems: [
                        {
                          horizontalSizeStyle: 'FILL_AVAILABLE_SPACE',
                          widgets: [
                            {
                              decoratedText: {
                                topLabel: '당일 매출금액',
                                text: dayRevenue[0].payAmount.toString() + '원',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '당월 매출금액',
                                text:
                                  monthRevenue[0].payAmount.toString() + '원',
                              },
                            },
                          ],
                        },
                        {
                          horizontalSizeStyle: 'FILL_AVAILABLE_SPACE',
                          widgets: [
                            {
                              decoratedText: {
                                topLabel: '당일 취소금액',
                                text:
                                  dayRevenue[0].cancelAmount.toString() + '원',
                              },
                            },
                            {
                              decoratedText: {
                                topLabel: '당월 취소금액',
                                text:
                                  monthRevenue[0].cancelAmount.toString() +
                                  '원',
                              },
                            },
                          ],
                        },
                      ],
                    },
                  },
                ],
              },
              {
                header: '분석통계',
                widgets: [
                  {
                    textParagraph: {
                      text:
                        `*당일 요청:* ${analysisStatistics.day.totalCount.toString()}건\n` +
                        `*실패:* ${analysisStatistics.day.failedCount.toString()}건 (인증: ${analysisStatistics.day.authFailedCount.toString()}, 분석: ${analysisStatistics.day.analysisFailedCount.toString()}, 기타: ${analysisStatistics.day.etcCount.toString()})\n\n` +
                        `*당월 요청:* ${analysisStatistics.month.totalCount.toString()}건\n` +
                        `*실패:* ${analysisStatistics.month.failedCount.toString()}건 (인증: ${analysisStatistics.month.authFailedCount.toString()}, 분석: ${analysisStatistics.month.analysisFailedCount.toString()}, 기타: ${analysisStatistics.month.etcCount.toString()})`,
                    },
                  },
                ],
              },
            ],
          },
        },
      ],
    };

    try {
      await axios.post(this.googleChatWebHookUrl ?? '', payload);
    } catch (error) {
      if (axios.isAxiosError(error)) {
        console.log('에러 응답: ', error.response?.data);
        console.log('HTTP 상태코드', error.response?.status);
      }
    }

    return;
  }

  // 날짜 포멧팅
  private formatDateRange(dateType: 'month' | 'day', date?: string) {
    const base = date ? dayjs(date) : dayjs();
    const dateRange = dateType === 'month' ? 'year' : 'month';

    return {
      startDate: base.startOf(dateRange).format('YYYY-MM-DD HH:mm:ss'),
      endDate: base.endOf(dateRange).format('YYYY-MM-DD HH:mm:ss'),
    };
  }

  // 비율계산
  private calculateRate(part: number, total: number): number {
    return total === 0 ? 0 : Number(((part / total) * 100).toFixed(2));
  }

  // 전체 월 범위 생성
  private generateMonthRange(startDate: string, endDate: string): string[] {
    const start = dayjs(startDate).startOf('month');
    const end = dayjs(endDate).endOf('month');
    const months: string[] = [];

    let current = start;
    while (current.isBefore(end) || current.isSame(end, 'month')) {
      months.push(current.format('YYYY-MM'));
      current = current.add(1, 'month');
    }

    return months;
  }

  // 전체 일 범위 생성
  private generateDayRange(startDate: string, endDate: string): string[] {
    const start = dayjs(startDate).startOf('day');
    const end = dayjs(endDate).endOf('day');
    const days: string[] = [];

    let current = start;
    while (current.isBefore(end) || current.isSame(end, 'day')) {
      days.push(current.format('YYYY-MM-DD'));
      current = current.add(1, 'day');
    }

    return days;
  }

  // 분석결과별 통계
  private calculateAnalysisStatisticsCount(
    statistics: Awaited<
      ReturnType<StatisticsRepository['getAnalysisStatistics']>
    >,
  ) {
    return statistics.reduce(
      (acc, curr) => ({
        total: acc.total + curr.total,
        successTotal: acc.successTotal + curr.successTotal,
        failureTotal: acc.failureTotal + curr.failedTotal,
        authFailureTotal: acc.authFailureTotal + curr.authFailed,
        analysisFailureTotal: acc.analysisFailureTotal + curr.analysisFailed,
        etcTotal: acc.etcTotal + curr.etc,
      }),
      {
        total: 0,
        successTotal: 0,
        failureTotal: 0,
        authFailureTotal: 0,
        analysisFailureTotal: 0,
        etcTotal: 0,
      },
    );
  }

  // 가입통계 상세 리스트 생성
  private buildSignUpStatisticsDetails(
    dateType: 'month' | 'day',
    statistics: Awaited<
      ReturnType<StatisticsRepository['getSignUpStatistics']>
    >,
    dates: string[],
  ) {
    const statsMap = new Map(statistics.map((stat) => [stat.date, stat]));

    // dates 배열의 첫 번째 날짜의 이전 달/일 계산
    const firstDate = dates[0];
    const prevDate =
      dateType === 'month'
        ? dayjs(firstDate).subtract(1, 'month').format('YYYY-MM')
        : dayjs(firstDate).subtract(1, 'day').format('YYYY-MM-DD');

    return [prevDate, ...dates].map((date) => {
      const statistic = statsMap.get(date);

      if (!statistic) {
        return {
          date,
          signupCount: 0,
          resignCount: 0,
        };
      }

      const { signupCount, resignCount } = statistic;

      return {
        date,
        signupCount,
        resignCount,
      };
    });
  }

  // 매출결과 상세 리스트 생성
  private buildRevenueStatisticsDetails(
    statistics: Awaited<
      ReturnType<StatisticsRepository['getRevenueStatistics']>
    >,
    dates: string[],
  ) {
    const statsMap = new Map(statistics.map((stat) => [stat.date, stat]));

    return dates.map((date) => {
      const statistic = statsMap.get(date);

      if (!statistic) {
        return {
          date,
          payAmount: 0,
          cancelAmount: 0,
        };
      }

      const { payAmount, cancelAmount } = statistic;

      return {
        date,
        payAmount,
        cancelAmount,
      };
    });
  }

  // 분석결과 상세 리스트 생성
  private buildAnalysisStatisticsDetails(
    statistics: Awaited<
      ReturnType<StatisticsRepository['getAnalysisStatistics']>
    >,
    dates: string[],
  ) {
    const statsMap = new Map(statistics.map((stat) => [stat.date, stat]));

    return dates.map((date) => {
      const statistic = statsMap.get(date);

      if (!statistic) {
        return {
          date,
          totalCount: 0,
          successCount: 0,
          failureCount: 0,
          successRate: 0,
          failureRate: 0,
        };
      }

      const { total, successTotal, failedTotal } = statistic;

      return {
        date,
        totalCount: total,
        successCount: successTotal,
        failureCount: failedTotal,
        successRate: this.calculateRate(successTotal, total),
        failureRate: this.calculateRate(failedTotal, total),
      };
    });
  }

  // 전월/전일 비율 계산
  private calculateSignupStatisticsRate(
    dataList: Awaited<ReturnType<StatisticsRepository['getSignUpStatistics']>>,
  ) {
    return dataList.slice(1).map((data, i) => {
      const prev = dataList[i];

      return {
        date: data.date,
        signupCount: data.signupCount,
        signupRate: this.signCalculateRate(data.signupCount, prev.signupCount),
        resignCount: data.resignCount,
        resignRate: this.signCalculateRate(data.resignCount, prev.resignCount),
      };
    });
  }

  //* 월별/일별 통계 비율 산정
  private signCalculateRate(month: number, prev: number): number {
    return prev === 0 ? 0 : Number((((month - prev) / prev) * 100).toFixed(2));
  }

  //* 가입자 통계 데이터 구분(전월, 전일, 당월, 당일)
  private signupDateDivision(
    statistics: Awaited<
      ReturnType<StatisticsRepository['getSignUpStatistics']>
    >,
    date: string,
    dateType: 'month' | 'day',
  ) {
    //* 전월, 월, 전일, 일 구분
    const before = dayjs(date).subtract(1, dateType);

    const nowData = statistics.filter((e) =>
      dayjs(e.date).isSame(date, dateType),
    );

    const result =
      nowData.length > 0
        ? nowData
        : [{ date: date, signupCount: 0, resignCount: 0 }];

    const beforeData = statistics.filter((e) =>
      dayjs(e.date).isSame(before, dateType),
    ) ?? { date: before, signupCount: 0, resignCount: 0 };

    const beforeResult =
      beforeData.length > 0
        ? beforeData
        : [{ date: before, signupCount: 0, resignCount: 0 }];

    return { beforeResult, result };
  }

  //* 일별 결제/결제취소 리스트 생성
  private buildPayDailys(
    payDailys: Awaited<ReturnType<StatisticsRepository['getPayDaily']>>,
    dates: string[],
  ) {
    const payDailysMap = new Map(payDailys.map((pay) => [pay.date, pay]));

    return dates.map((date) => {
      const payDaily = payDailysMap.get(date);

      if (!payDaily) {
        return {
          date,
          count: 0,
          totalAmount: 0,
        };
      }

      const { count, totalAmount } = payDaily;

      return {
        date,
        count,
        totalAmount,
      };
    });
  }

  //* 구글 챗 rich card 형태로 포맷팅
  private googleChatPayloadFormat() {}
}
