import { translateChecked } from '@api/helpers/translate.helper';
import { DrizzleService } from '@lib/drizzle/src/drizzle.service';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import dayjs from 'dayjs';
import { and, asc, count, desc, eq, gte, lte, sql, SQL } from 'drizzle-orm';
import { tnAlarm, tnMember, tnPush } from 'drizzle/schema';
import { AlarmCheckDto, AlarmCreatedDto, AlarmListDto } from './alarm.dto';

@Injectable()
export class AlarmRepository {
  private key: string;
  constructor(
    private drizzle: DrizzleService,
    private configService: ConfigService,
  ) {
    this.key = this.configService.get<string>('aes.secret') ?? '';
  }

  async create(dto: AlarmCreatedDto) {
    const almIds = await this.drizzle
      .insert(tnAlarm)
      .values({
        ...dto,
        almUserName: dto.almUserName
          ? sql`FN_ENCRYPT(${dto.almUserName}, ${this.key})` // sql`TO_BASE64(AES_ENCRYPT(${dto.almUserName},${this.key}))`
          : undefined,
      })
      .$returningId();

    return almIds;
  }

  //* 읽음 여부는 보지 않는다 — 이미 읽은 알림의 재요청도 소유면 통과한다.
  async owned(almId: number, memId: number): Promise<boolean> {
    const rows = await this.drizzle
      .select({ almId: tnAlarm.almId })
      .from(tnAlarm)
      .where(
        and(
          eq(tnAlarm.almId, almId),
          //* mut:k11-owned begin
          eq(tnAlarm.memId, memId),
          //* mut:k11-owned end
        ),
      );
    return rows.length > 0;
  }

  async delete(dto: AlarmCheckDto) {
    await this.drizzle.delete(tnAlarm).where(eq(tnAlarm.almId, dto.almId));
  }

  async check(dto: AlarmCheckDto) {
    await this.drizzle
      .update(tnAlarm)
      .set({
        almChecked: 1,
      })
      .where(eq(tnAlarm.almId, dto.almId));
  }

  async getList(
    where: SQL<unknown> | undefined,
    orderBy: SQL<unknown>,
    page: number,
  ) {
    const limit = 10;
    const offset = (+page - 1) * limit;

    return await this.drizzle
      .select({
        almId: tnAlarm.almId,
        almType: sql`GET_STATUS(${tnAlarm.almType})`,
        almUserName: sql<string>`FN_DECRYPT(${tnAlarm.almUserName}, ${this.key})`,
        almTitle: tnAlarm.almTitle,
        almContent: tnAlarm.almContent,
        almMessage: tnAlarm.almMessage,
        almChecked: translateChecked(tnAlarm.almChecked).as('almChecked'),
        almRedirectUrl: tnPush.pshUrl,
        pshId: tnAlarm.almRedirectId,
        almCreatedAt: tnAlarm.almCreatedAt,
        almUpdatedAt: tnAlarm.almUpdatedAt,
      })
      .from(tnAlarm)
      .leftJoin(tnMember, eq(tnMember.memId, tnAlarm.memId))
      .leftJoin(tnPush, eq(tnPush.pshId, tnAlarm.almRedirectId))
      .where(where)
      .orderBy(orderBy)
      .limit(limit)
      .offset(offset);
  }

  async count(where: SQL<unknown> | undefined) {
    const [totalCount] = await this.drizzle
      .select({ count: count() })
      .from(tnAlarm)
      .leftJoin(tnMember, eq(tnMember.memId, tnAlarm.memId))
      .where(where);

    return totalCount.count ?? 0;
  }

  async noCheckCount(id: number) {
    const nowDate = dayjs();
    const subDate = nowDate.subtract(1, 'month').format('YYYY-MM-DD HH:mm:ss');
    const [totalCount] = await this.drizzle
      .select({ count: count() })
      .from(tnAlarm)
      .where(
        and(
          eq(tnAlarm.memId, id),
          eq(tnAlarm.almChecked, 0),
          gte(tnAlarm.almCreatedAt, subDate),
        ),
      );

    return totalCount.count ?? 0;
  }

  async whereClause(dto: AlarmListDto) {
    const { memId, dateType, startDate, endDate, almType } = dto;
    //* 3개월

    const where: SQL[] = [];

    if (!!memId) {
      const whereId = eq(tnAlarm.memId, memId);
      where.push(whereId);
    }

    if (!!startDate && !!endDate) {
      const dateWhere = this.makeDateWhere(dateType, startDate, endDate);
      where.push(...dateWhere);
    }

    // 알림 타입 필터 (SND001: 결제, SND002: 분석, SND003: 팩스, SND004: 이벤트)
    if (!!almType) {
      where.push(eq(tnAlarm.almType, almType));
    }

    return and(...where);
  }

  async userOrderByClause() {
    return asc(tnAlarm.almChecked), desc(tnAlarm.almId);
  }

  makeDateWhere(dateType: string, startDate: string, endDate: string): SQL[] {
    const where: SQL[] = [];

    switch (dateType) {
      case 'almCreatedAt': {
        where.push(gte(tnAlarm.almCreatedAt, startDate));
        where.push(lte(tnAlarm.almCreatedAt, endDate));
        return where;
      }
      default: {
        where.push(gte(tnAlarm.almCreatedAt, startDate));
        where.push(lte(tnAlarm.almCreatedAt, endDate));
        return where;
      }
    }
  }
}
