import {
  ConflictException,
  Injectable,
  NotFoundException,
  UnauthorizedException,
  Logger,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserModel } from '../user/entities/user.entity';
import { FarmModel } from '../farm/entities/farm.entity';
import { GenomeRequestModel } from '../genome/entities/genome-request.entity';
import { Repository, IsNull } from 'typeorm';
import { LoginDto } from './dto/login.dto';
import { LoginResponseDto } from './dto/login-response.dto';
import { SignupDto } from './dto/signup.dto';
import { SignupResponseDto } from './dto/signup-response.dto';
import { SendFindIdCodeDto } from './dto/send-find-id-code.dto';
import { VerifyFindIdCodeDto } from './dto/verify-find-id-code.dto';
import { FindIdResponseDto } from './dto/find-id-response.dto';
import { SendResetPasswordCodeDto } from './dto/send-reset-password-code.dto';
import { VerifyResetPasswordCodeDto } from './dto/verify-reset-password-code.dto';
import { ResetPasswordDto } from './dto/reset-password.dto';
import { ResetPasswordResponseDto } from './dto/reset-password-response.dto';
import { SendSignupCodeDto } from './dto/send-signup-code.dto';
import { VerifySignupCodeDto } from './dto/verify-signup-code.dto';

import { EmailService } from 'src/shared/email/email.service';
import { VerificationService } from 'src/shared/verification/verification.service';
import { VERIFICATION_CONFIG } from 'src/common/config/VerificationConfig';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class AuthService {
  private readonly logger = new Logger(AuthService.name);

  constructor(
    @InjectRepository(UserModel)
    private readonly userRepository: Repository<UserModel>,
    @InjectRepository(FarmModel)
    private readonly farmRepository: Repository<FarmModel>,
    @InjectRepository(GenomeRequestModel)
    private readonly genomeRequestRepository: Repository<GenomeRequestModel>,
    private readonly emailService: EmailService,
    private readonly verificationService: VerificationService,
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
  ) { }

  /**
   * 유저 로그인
   */
  async login(loginDto: LoginDto): Promise<LoginResponseDto> {
    const { userId, userPassword } = loginDto;
    this.logger.log(`[LOGIN] 로그인 시도 - userId: ${userId}`);

    const user = await this.userRepository.findOne({
      where: { userId },
    });

    if (!user) {
      this.logger.warn(`[LOGIN] 사용자 없음 - userId: ${userId}`);
      throw new UnauthorizedException('아이디 또는 비밀번호가 틀렸습니다');
    }

    const isPasswordValid = await bcrypt.compare(userPassword, user.userPw);
    const inputHash = await bcrypt.hash(userPassword, 10);
  this.logger.log(`[DEBUG] 입력 해시: ${inputHash}, DB 해시: ${user.userPw}`);
    if (!isPasswordValid) {
      this.logger.warn(`[LOGIN] 비밀번호 불일치 - userId: ${userId}`);
      throw new UnauthorizedException('아이디 또는 비밀번호가 틀렸습니다');
    }

    if (user.delDt !== null) {
      this.logger.warn(`[LOGIN] 탈퇴 계정 - userId: ${userId}`);
      throw new UnauthorizedException('탈퇴한 계정입니다');
    }

    const payload = {
      userId: user.userId,
      userNo: user.pkUserNo,
    };

    const accessToken = this.jwtService.sign(payload as any);

    // 최근 검사 년도 조회
    const defaultAnalysisYear = await this.getDefaultAnalysisYear(user.pkUserNo);

    this.logger.log(`[LOGIN] 로그인 성공 - userId: ${userId}, defaultAnalysisYear: ${defaultAnalysisYear}`);

    return {
      message: '로그인 성공',
      accessToken,
      user: {
        pkUserNo: user.pkUserNo,
        userId: user.userId,
        userName: user.userName,
        userEmail: user.userEmail,
        userRole: user.userRole || 'USER',
      },
      defaultAnalysisYear,
    };
  }

  /**
   * 사용자의 최근 검사 년도 조회
   * @param userNo - 사용자 번호
   * @returns 최근 검사 년도 (없으면 현재 년도)
   */
  private async getDefaultAnalysisYear(userNo: number): Promise<number> {
    try {
      // 1. 사용자의 농장 번호 조회
      const farm = await this.farmRepository.findOne({
        where: { fkUserNo: userNo, delDt: IsNull() },
        select: ['pkFarmNo'],
      });

      if (!farm) {
        this.logger.log(`[getDefaultAnalysisYear] userNo: ${userNo}, No farm found, returning current year`);
        return new Date().getFullYear();
      }

      // 2. 농장의 검사 이력에서 최신 날짜 조회
      const result = await this.genomeRequestRepository
        .createQueryBuilder('request')
        .select('MAX(request.chipReportDt)', 'maxChipDt')
        .addSelect('MAX(request.msReportDt)', 'maxMsDt')
        .where('request.fkFarmNo = :farmNo', { farmNo: farm.pkFarmNo })
        .andWhere('request.delDt IS NULL')
        .getRawOne();

      const maxChipDt = result?.maxChipDt ? new Date(result.maxChipDt) : null;
      const maxMsDt = result?.maxMsDt ? new Date(result.maxMsDt) : null;

      // 둘 중 최신 날짜 선택
      let latestDate: Date | null = null;
      if (maxChipDt && maxMsDt) {
        latestDate = maxChipDt > maxMsDt ? maxChipDt : maxMsDt;
      } else if (maxChipDt) {
        latestDate = maxChipDt;
      } else if (maxMsDt) {
        latestDate = maxMsDt;
      }

      const year = latestDate ? latestDate.getFullYear() : new Date().getFullYear();
      
      this.logger.log(`[getDefaultAnalysisYear] userNo: ${userNo}, farmNo: ${farm.pkFarmNo}, maxChipDt: ${maxChipDt?.toISOString()}, maxMsDt: ${maxMsDt?.toISOString()}, year: ${year}`);

      return year;
    } catch (error) {
      this.logger.error(`[getDefaultAnalysisYear] Error: ${error.message}`);
      return new Date().getFullYear();
    }
  }

  /**
   * 회원가입
   */
  async register(signupDto: SignupDto, clientIp: string): Promise<SignupResponseDto> {
    const { userId, userEmail, userPhone } = signupDto;
    this.logger.log(`[REGISTER] 회원가입 시도 - userId: ${userId}, email: ${userEmail}`);

    const verifiedKey = `signup-verified:${userEmail}`;
    const isEmailVerified = await this.verificationService.verifyCode(verifiedKey, 'true');

    if (!isEmailVerified) {
      this.logger.warn(`[REGISTER] 이메일 미인증 - email: ${userEmail}`);
      throw new UnauthorizedException('이메일 인증이 완료되지 않았습니다');
    }

    const whereConditions = [{ userId }, { userEmail }, { userPhone }];
    const existingUser = await this.userRepository.findOne({
      where: whereConditions,
    });

    if (existingUser) {
      if (existingUser.userId === userId) {
        throw new ConflictException('이미 사용 중인 아이디입니다');
      }
      if (existingUser.userEmail === userEmail) {
        throw new ConflictException('이미 사용 중인 이메일입니다');
      }
      if (existingUser.userPhone === userPhone) {
        throw new ConflictException('이미 사용 중인 전화번호입니다');
      }
    }

    const saltRounds = parseInt(this.configService.get<string>('BCRYPT_SALT_ROUNDS') || '10', 10);
    const hashedPassword = await bcrypt.hash(signupDto.userPassword, saltRounds);

    const newUser = this.userRepository.create({
      userId: signupDto.userId,
      userPw: hashedPassword,
      userName: signupDto.userName,
      userPhone: signupDto.userPhone,
      userEmail: signupDto.userEmail,
      regIp: clientIp,
      regUserId: signupDto.userId,
    });

    const savedUser = await this.userRepository.save(newUser);
    this.logger.log(`[REGISTER] 회원가입 성공 - userId: ${savedUser.userId}`);

    return {
      message: '회원가입이 완료되었습니다',
      redirectUrl: '/dashboard',
      userId: savedUser.userId,
      userNo: savedUser.pkUserNo,
    };
  }

  /**
   * 이메일 중복 체크
   */
  async checkEmailDuplicate(userEmail: string): Promise<{
    available: boolean;
    message: string;
  }> {
    const existingUser = await this.userRepository.findOne({
      where: { userEmail },
    });

    if (existingUser) {
      return {
        available: false,
        message: '이미 사용 중인 이메일입니다',
      };
    }

    return {
      available: true,
      message: '사용 가능한 이메일입니다',
    };
  }

  /**
   * 회원가입 - 이메일 인증번호 발송
   */
  async sendSignupCode(dto: SendSignupCodeDto): Promise<{
    success: boolean;
    message: string;
    expiresIn: number;
  }> {
    const { userEmail } = dto;
    this.logger.log(`[SEND-CODE] ========== 인증번호 발송 시작 ==========`);
    this.logger.log(`[SEND-CODE] 이메일: ${userEmail}`);
    process.stdout.write(`[SEND-CODE] 이메일: ${userEmail}\n`);

    try {
      // 1. 이메일 중복 체크
      this.logger.log(`[SEND-CODE] Step 1: 이메일 중복 체크`);
      const existingUser = await this.userRepository.findOne({
        where: { userEmail },
      });

      if (existingUser) {
        this.logger.warn(`[SEND-CODE] 이메일 중복 - ${userEmail}`);
        throw new ConflictException('이미 사용 중인 이메일입니다');
      }
      this.logger.log(`[SEND-CODE] Step 1 완료: 중복 없음`);

      // 2. 인증번호 생성
      this.logger.log(`[SEND-CODE] Step 2: 인증번호 생성`);
      const code = this.verificationService.generateCode();
      this.logger.log(`[SEND-CODE] 생성된 인증번호: ${code}`);
      process.stdout.write(`[SEND-CODE] 생성된 인증번호: ${code}\n`);

      // 3. Redis에 저장
      this.logger.log(`[SEND-CODE] Step 3: Redis 저장`);
      const key = `signup:${userEmail}`;
      await this.verificationService.saveCode(key, code);
      this.logger.log(`[SEND-CODE] Redis 저장 완료 - key: ${key}`);

      // 4. 이메일 발송
      this.logger.log(`[SEND-CODE] Step 4: 이메일 발송 시작`);
      await this.emailService.sendVerificationCode(userEmail, code);
      this.logger.log(`[SEND-CODE] 이메일 발송 완료`);

      this.logger.log(`[SEND-CODE] ========== 인증번호 발송 성공 ==========`);

      return {
        success: true,
        message: '인증번호가 이메일로 발송되었습니다',
        expiresIn: VERIFICATION_CONFIG.CODE_EXPIRY_SECONDS,
      };
    } catch (error) {
      this.logger.error(`[SEND-CODE] ========== 에러 발생 ==========`);
      this.logger.error(`[SEND-CODE] Error Name: ${error.name}`);
      this.logger.error(`[SEND-CODE] Error Message: ${error.message}`);
      this.logger.error(`[SEND-CODE] Stack: ${error.stack}`);
      process.stdout.write(`[SEND-CODE] ERROR: ${error.message}\n`);
      process.stdout.write(`[SEND-CODE] STACK: ${error.stack}\n`);
      throw error;
    }
  }

  /**
   * 회원가입 - 이메일 인증번호 검증
   */
  async verifySignupCode(dto: VerifySignupCodeDto): Promise<{
    success: boolean;
    message: string;
    verified: boolean;
  }> {
    const { userEmail, code } = dto;
    this.logger.log(`[VERIFY-CODE] 인증번호 검증 - email: ${userEmail}`);

    const key = `signup:${userEmail}`;
    const isValid = await this.verificationService.verifyCode(key, code);

    if (!isValid) {
      this.logger.warn(`[VERIFY-CODE] 인증 실패 - email: ${userEmail}`);
      throw new UnauthorizedException('인증번호가 일치하지 않거나 만료되었습니다');
    }

    const verifiedKey = `signup-verified:${userEmail}`;
    await this.verificationService.saveCode(verifiedKey, 'true');

    this.logger.log(`[VERIFY-CODE] 인증 성공 - email: ${userEmail}`);

    return {
      success: true,
      message: '이메일 인증이 완료되었습니다',
      verified: true,
    };
  }

  /**
   * 아이디 찾기 - 인증번호 발송
   */
  async sendFindIdCode(
    dto: SendFindIdCodeDto,
  ): Promise<{ success: boolean; message: string; expiresIn: number }> {
    const { userName, userEmail } = dto;
    this.logger.log(`[FIND-ID] 인증번호 발송 - name: ${userName}, email: ${userEmail}`);

    const user = await this.userRepository.findOne({
      where: { userName, userEmail },
    });

    if (!user) {
      throw new NotFoundException('일치하는 사용자 정보를 찾을 수 없습니다');
    }

    const code = this.verificationService.generateCode();
    const key = `find-id:${userEmail}`;
    await this.verificationService.saveCode(key, code);
    await this.emailService.sendVerificationCode(userEmail, code);

    this.logger.log(`[FIND-ID] 인증번호 발송 완료 - email: ${userEmail}`);

    return {
      success: true,
      message: '인증번호가 이메일로 발송되었습니다',
      expiresIn: VERIFICATION_CONFIG.CODE_EXPIRY_SECONDS,
    };
  }

  /**
   * 아이디 찾기 - 인증번호 검증
   */
  async verifyFindIdCode(dto: VerifyFindIdCodeDto): Promise<FindIdResponseDto> {
    const { userEmail, verificationCode } = dto;
    this.logger.log(`[FIND-ID] 인증번호 검증 - email: ${userEmail}`);

    const key = `find-id:${userEmail}`;
    const isValid = await this.verificationService.verifyCode(key, verificationCode);

    if (!isValid) {
      throw new UnauthorizedException('인증번호가 일치하지 않거나 만료되었습니다');
    }

    const user = await this.userRepository.findOne({
      where: { userEmail },
    });

    if (!user) {
      throw new NotFoundException('사용자를 찾을 수 없습니다');
    }

    const maskedUserId = this.maskUserId(user.userId);

    return {
      message: '인증이 완료되었습니다',
      userId: user.userId,
      maskedUserId,
    };
  }

  private maskUserId(userId: string): string {
    if (userId.length <= 4) {
      return userId;
    }
    const visiblePart = userId.substring(0, 4);
    const maskedPart = '*'.repeat(userId.length - 4);
    return visiblePart + maskedPart;
  }

  /**
   * 비밀번호 재설정 - 인증번호 발송
   */
  async sendResetPasswordCode(
    dto: SendResetPasswordCodeDto,
  ): Promise<{ success: boolean; message: string; expiresIn: number }> {
    const { userId, userEmail } = dto;
    this.logger.log(`[RESET-PW] 인증번호 발송 - userId: ${userId}, email: ${userEmail}`);

    const user = await this.userRepository.findOne({
      where: { userId, userEmail },
    });

    if (!user) {
      throw new NotFoundException('일치하는 사용자 정보를 찾을 수 없습니다');
    }

    const code = this.verificationService.generateCode();
    const key = `reset-pw:${userEmail}`;
    await this.verificationService.saveCode(key, code);
    await this.emailService.sendVerificationCode(userEmail, code);

    this.logger.log(`[RESET-PW] 인증번호 발송 완료 - email: ${userEmail}`);

    return {
      success: true,
      message: '인증번호가 이메일로 발송되었습니다',
      expiresIn: VERIFICATION_CONFIG.CODE_EXPIRY_SECONDS,
    };
  }

  /**
   * 비밀번호 재설정 - 인증번호 검증
   */
  async verifyResetPasswordCode(
    dto: VerifyResetPasswordCodeDto,
  ): Promise<{ success: boolean; message: string; resetToken: string }> {
    const { userId, userEmail, verificationCode } = dto;
    this.logger.log(`[RESET-PW] 인증번호 검증 - userId: ${userId}, email: ${userEmail}`);

    const key = `reset-pw:${userEmail}`;
    const isValid = await this.verificationService.verifyCode(key, verificationCode);

    if (!isValid) {
      throw new UnauthorizedException('인증번호가 일치하지 않거나 만료되었습니다');
    }

    const user = await this.userRepository.findOne({
      where: { userId, userEmail },
    });

    if (!user) {
      throw new NotFoundException('사용자를 찾을 수 없습니다');
    }

    const resetToken = await this.verificationService.generateResetToken(userId);

    return {
      success: true,
      message: '인증이 완료되었습니다',
      resetToken,
    };
  }

  /**
   * 비밀번호 재설정 - 새 비밀번호로 변경
   */
  async resetPassword(dto: ResetPasswordDto): Promise<ResetPasswordResponseDto> {
    const { resetToken, newPassword } = dto;
    this.logger.log(`[RESET-PW] 비밀번호 변경 시도`);

    const userId = await this.verificationService.verifyResetToken(resetToken);

    if (!userId) {
      throw new UnauthorizedException('유효하지 않거나 만료된 토큰입니다');
    }

    const user = await this.userRepository.findOne({
      where: { userId },
    });

    if (!user) {
      throw new NotFoundException('사용자를 찾을 수 없습니다');
    }

    const saltRounds = parseInt(this.configService.get<string>('BCRYPT_SALT_ROUNDS') || '10', 10);
    const hashedPassword = await bcrypt.hash(newPassword, saltRounds);

    user.userPw = hashedPassword;
    await this.userRepository.save(user);

    this.logger.log(`[RESET-PW] 비밀번호 변경 완료 - userId: ${userId}`);

    return {
      message: '비밀번호가 변경되었습니다',
    };
  }
}
