import { ALIGO_REQUEST_FAILED } from '@api/constants/api.constant';
import { WebClientService } from '@lib/webclient/src';
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { plainToClass } from 'class-transformer';
import {
  AlimtalkSendCheckResponseDto,
  SendResponseDto,
  SmsSendCheckResponseDto,
} from './aligo.dto';
import { ALigoException } from './aligo.exception';
import {
  AlimtalkSendCheckDto,
  AlimtalkSendDto,
  SmsSendCheckDto,
} from './aligo.interface';

//* 알리고 문자 발송 API — 알림톡과 같은 계정 키(key/user_id)를 쓴다.
const SMS_SEND_URL = 'https://apis.aligo.in/send/';

@Injectable()
export class AligoService {
  private logger = new Logger(AligoService.name);
  private apiKey: string;
  private senderKey: string;
  private userId: string;
  private baseUrl: string;
  private forceSms: boolean;
  constructor(
    private configService: ConfigService,
    private webClientService: WebClientService,
  ) {
    this.apiKey = this.configService.get<string>('aligo.apiKey') ?? '';
    this.senderKey = this.configService.get<string>('aligo.senderKey') ?? '';
    this.userId = this.configService.get<string>('aligo.userId') ?? '';
    this.baseUrl = this.configService.get<string>('aligo.baseUrl') ?? '';
    //* 핫픽스(2026-10-15): 알리고 알림톡이 접수 후 대기(W000)에 멈춰 대체문자도 안 나간다.
    //*   ALIGO_FORCE_SMS=Y 면 알림톡 대신 대체문자 내용을 문자로 바로 보낸다.
    //*   롤백: env 에서 ALIGO_FORCE_SMS 를 지우고 재배포하면 알림톡 발송으로 돌아간다.
    this.forceSms = this.configService.get<string>('ALIGO_FORCE_SMS') === 'Y';
  }

  /** 알림톡 발송 요청 */
  async send(dto: AlimtalkSendDto): Promise<SendResponseDto> {
    // local 환경에서는 실제 발송 없이 성공 응답 반환
    const nodeEnv = this.configService.get<string>('NODE_ENV');
    if (nodeEnv === 'local') {
      this.logger.log(`[LOCAL MODE] 알림톡 발송 스킵 - receiver: ${dto.receiver_1}, tpl_code: ${dto.tpl_code}`);
      return plainToClass(SendResponseDto, {
        code: 0,
        message: '[LOCAL] 발송 성공 (테스트 모드)',
        info: {
          type: 'AT',
          mid: Date.now(), // mock mid
          current: '99999',
          unit: 8,
          total: 8,
          scnt: 1,
          fcnt: 0,
        },
      });
    }

    if (this.forceSms) return this.sendSms(dto);

    const endPont = this.configService.get<string>('aligo.alimtalkEndPoint');
    const url = this.baseUrl + endPont;
    const apiData = {
      ...dto,
      apikey: this.apiKey,
      senderkey: this.senderKey,
      userid: this.userId,
    };

    const data = await this.webClientService
      .post<SendResponseDto>(url, apiData, {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        timeout: 30 * 1000,
        maxRedirects: 1,
      })
      .catch((e) => {
        this.logger.error(e);
        throw new ALigoException(ALIGO_REQUEST_FAILED);
      });
    return plainToClass(SendResponseDto, data);
  }

  /** 알림톡 요청을 대체문자 내용 그대로 문자(LMS)로 발송하고, 응답을 알림톡 응답 모양으로 맞춘다 */
  private async sendSms(dto: AlimtalkSendDto): Promise<SendResponseDto> {
    const apiData = {
      key: this.apiKey,
      user_id: this.userId,
      sender: dto.sender.replace(/\D/g, ''),
      receiver: dto.receiver_1,
      // ponytail: LMS 고정 — 호출부 대체문자가 전부 링크 포함 90byte 초과다. 짧은 문구가 생기면 길이로 SMS/LMS 를 고른다.
      msg_type: 'LMS',
      title: dto.fsubject_1 ?? dto.subject_1,
      msg: dto.fmessage_1 ?? dto.message_1,
      testmode_yn: dto.testMode,
    };

    const data = await this.webClientService
      .post<{
        result_code: number | string;
        message: string;
        msg_id?: number | string;
        success_cnt?: number;
        error_cnt?: number;
        msg_type?: string;
      }>(SMS_SEND_URL, apiData, {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        timeout: 30 * 1000,
        maxRedirects: 1,
      })
      .catch((e) => {
        this.logger.error(e);
        throw new ALigoException(ALIGO_REQUEST_FAILED);
      });
    //* 문자 API 는 result_code 1 이 성공이다. mid 는 비워 둔다 — 알림톡 상태 스케줄러는
    //*   altMid 가 있는 행만 알림톡 이력으로 조회하므로, 문자 msg_id 로 엉뚱한 알림톡을 읽지 않게 한다.
    return plainToClass(SendResponseDto, {
      code: Number(data.result_code) === 1 ? 0 : Number(data.result_code),
      message: data.message,
      info: {
        type: data.msg_type,
        scnt: data.success_cnt,
        fcnt: data.error_cnt,
      },
    });
  }

  //* 알림톡 발송완료여부확인
  async sendCheckforAlimtalk(
    dto: AlimtalkSendCheckDto,
  ): Promise<AlimtalkSendCheckResponseDto> {
    const endPont = this.configService.get<string>(
      'aligo.historyDetailEndPoint',
    );
    const url = this.baseUrl + endPont;
    const apiData = {
      ...dto,
      apikey: this.apiKey,
      userid: this.userId,
    };

    const data = await this.webClientService
      .post<AlimtalkSendCheckResponseDto>(url, apiData, {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        timeout: 30 * 1000,
        maxRedirects: 1,
      })
      .catch((e) => {
        this.logger.error(e);
        throw new ALigoException(ALIGO_REQUEST_FAILED);
      });
    return plainToClass(AlimtalkSendCheckResponseDto, data);
  }

  //* 문자발송완료여부확인
  async sendCheckforSms(
    dto: SmsSendCheckDto,
  ): Promise<SmsSendCheckResponseDto> {
    const endPont =
      this.configService.get<string>('aligo.smsHistoryDetailEndPoint') ?? '';
    const url = endPont;
    const apiData = {
      ...dto,
      key: this.apiKey,
      user_id: this.userId,
    };

    const data = await this.webClientService
      .post<SmsSendCheckResponseDto>(url, apiData, {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        timeout: 30 * 1000,
        maxRedirects: 1,
      })
      .catch((e) => {
        this.logger.error(e);
        throw new ALigoException(ALIGO_REQUEST_FAILED);
      });
    return plainToClass(SmsSendCheckResponseDto, data);
  }
}
