import {
  Injectable,
  UnauthorizedException,
  BadRequestException,
  Logger,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import * as crypto from 'crypto';
import { User } from '@/entities/user.entity';
import { UserAccount } from '@/entities/user-account.entity';
import { MktInvitation } from '@/entities/mkt-invitation.entity';
import { MktUserInviteStats } from '@/entities/mkt-user-invite-stats.entity';
import { MktActivity } from '@/entities/mkt-activity.entity';
import {
  LoginByPhoneDto,
  LoginByPasswordDto,
  RegisterDto,
  WechatLoginDto,
  WechatPhoneLoginDto,
} from './dto/auth.dto';

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

  constructor(
    @InjectRepository(User)
    private userRepo: Repository<User>,
    @InjectRepository(UserAccount)
    private userAccountRepo: Repository<UserAccount>,
    @InjectRepository(MktInvitation)
    private invitationRepo: Repository<MktInvitation>,
    @InjectRepository(MktUserInviteStats)
    private inviteStatsRepo: Repository<MktUserInviteStats>,
    @InjectRepository(MktActivity)
    private activityRepo: Repository<MktActivity>,
    private jwtService: JwtService,
    private configService: ConfigService,
  ) {}

  async loginByPhone(dto: LoginByPhoneDto) {
    const user = await this.validateByPhone(dto.phone, dto.code);
    return this.generateToken(user);
  }

  async loginByPassword(dto: LoginByPasswordDto) {
    const user = await this.validateByPassword(dto.phone, dto.password);
    return this.generateToken(user);
  }

  async loginByWechat(dto: WechatLoginDto) {
    try {
      // 调用微信接口获取 openid 和 session_key
      const wechatData = await this.getWechatOpenid(dto.code);

      if (!wechatData.openid) {
        throw new BadRequestException('微信授权失败');
      }

      // 查找是否已存在该微信用户
      let user = await this.userRepo.findOne({
        where: { wechatOpenid: wechatData.openid },
      });

      const isNewUser = !user;

      if (!user) {
        // 自动创建新用户
        user = this.userRepo.create({
          wechatOpenid: wechatData.openid,
          wechatUnionid: wechatData.unionid,
          nickname: dto.nickname || `微信用户${wechatData.openid.slice(-4)}`,
          avatar: dto.avatar || '',
        });
        await this.userRepo.save(user);
        this.logger.log(`微信用户自动注册: openid=${wechatData.openid}`);

        // 自动创建用户账户
        await this.createUserAccount(user.id);

        // 生成用户的邀请码
        await this.createUserInviteCode(user.id);

        // 处理邀请码绑定（仅新用户）
        if (dto.inviteCode) {
          await this.handleInviteCode(user.id, dto.inviteCode);
        }
      } else {
        // 更新用户信息
        if (dto.nickname) user.nickname = dto.nickname;
        if (dto.avatar) user.avatar = dto.avatar;
        if (wechatData.unionid) user.wechatUnionid = wechatData.unionid;
        user.lastLoginAt = new Date();
        await this.userRepo.save(user);
      }

      return this.generateToken(user);
    } catch (error) {
      this.logger.error('微信登录失败', error);
      throw new BadRequestException('微信登录失败，请重试');
    }
  }

  async loginByWechatPhone(dto: WechatPhoneLoginDto) {
    try {
      // 1. 调用微信接口获取 session_key
      const wechatData = await this.getWechatOpenid(dto.code);

      if (!wechatData.session_key) {
        throw new BadRequestException('获取微信会话失败');
      }

      // 2. 解密手机号
      const phoneData = this.decryptWechatData(
        dto.encryptedData,
        dto.iv,
        wechatData.session_key,
      );

      if (!phoneData || !phoneData.phoneNumber) {
        throw new BadRequestException('解密手机号失败');
      }

      const phone = phoneData.phoneNumber;
      this.logger.log(`微信手机号登录: phone=${phone}, openid=${wechatData.openid}`);

      // 3. 查找或创建用户
      let user = await this.userRepo.findOne({ where: { phone } });

      const isNewUser = !user;

      if (!user) {
        // 自动创建新用户
        user = this.userRepo.create({
          phone,
          wechatOpenid: wechatData.openid,
          wechatUnionid: wechatData.unionid,
          nickname: `用户${phone.slice(-4)}`,
        });
        await this.userRepo.save(user);
        this.logger.log(`微信手机号自动注册: phone=${phone}`);

        // 自动创建用户账户
        await this.createUserAccount(user.id);

        // 生成用户的邀请码
        await this.createUserInviteCode(user.id);

        // 处理邀请码绑定（仅新用户）
        if (dto.inviteCode) {
          await this.handleInviteCode(user.id, dto.inviteCode);
        }
      } else {
        // 更新微信信息
        if (wechatData.openid && !user.wechatOpenid) {
          user.wechatOpenid = wechatData.openid;
        }
        if (wechatData.unionid && !user.wechatUnionid) {
          user.wechatUnionid = wechatData.unionid;
        }
        user.lastLoginAt = new Date();
        await this.userRepo.save(user);
      }

      return this.generateToken(user);
    } catch (error) {
      this.logger.error('微信手机号登录失败', error);
      throw new BadRequestException(
        error.message || '微信手机号登录失败，请重试',
      );
    }
  }

  async register(dto: RegisterDto) {
    const existing = await this.userRepo.findOne({
      where: { phone: dto.phone },
    });
    if (existing) {
      throw new BadRequestException('该手机号已注册');
    }

    const hashedPassword = dto.password
      ? await bcrypt.hash(dto.password, 10)
      : undefined;

    const user = this.userRepo.create({
      phone: dto.phone,
      password: hashedPassword,
      nickname: dto.nickname || `用户${dto.phone.slice(-4)}`,
    });

    await this.userRepo.save(user);

    // 自动创建用户账户
    await this.createUserAccount(user.id);

    // 生成用户的邀请码
    await this.createUserInviteCode(user.id);

    // 处理邀请码绑定
    if (dto.inviteCode) {
      await this.handleInviteCode(user.id, dto.inviteCode);
    }

    return this.generateToken(user);
  }

  sendCode(phone: string) {
    const code = Math.random().toString().slice(2, 8);
    this.cacheCode(phone, code);
    this.logger.log(`验证码已发送至 ${phone}: ${code}`);
    return { message: '验证码已发送' };
  }

  async refresh(refreshToken: string) {
    try {
      const payload: { sub: number; phone: string } =
        await this.jwtService.verifyAsync(refreshToken, {
          secret: this.configService.get<string>('jwt.secret'),
        });

      const user = await this.userRepo.findOne({ where: { id: payload.sub } });
      if (!user) {
        throw new UnauthorizedException('用户不存在');
      }

      return this.generateToken(user);
    } catch {
      throw new UnauthorizedException('刷新令牌无效');
    }
  }

  private async getWechatOpenid(code: string): Promise<{
    openid: string;
    session_key: string;
    unionid?: string;
  }> {
    // 开发模式：返回模拟数据
    const isDev = process.env.NODE_ENV === 'development';
    if (isDev) {
      this.logger.log(`[DEV] 模拟微信登录: code=${code}`);
      return {
        openid: `dev_openid_${Date.now()}`,
        session_key: 'dev_session_key_1234567890abcdef',
        unionid: `dev_unionid_${Date.now()}`,
      };
    }

    // 生产模式：调用微信API
    const appId = this.configService.get<string>('wechat.appId');
    const appSecret = this.configService.get<string>('wechat.appSecret');

    if (!appId || !appSecret) {
      throw new BadRequestException('微信配置未设置');
    }

    const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appId}&secret=${appSecret}&js_code=${code}&grant_type=authorization_code`;

    try {
      const response = await fetch(url);
      const data = await response.json();

      if (data.errcode) {
        this.logger.error(`微信API错误: ${data.errmsg}`);
        throw new BadRequestException('微信授权失败');
      }

      return {
        openid: data.openid,
        session_key: data.session_key,
        unionid: data.unionid,
      };
    } catch (error) {
      this.logger.error('调用微信API失败', error);
      throw new BadRequestException('微信授权失败');
    }
  }

  private decryptWechatData(
    encryptedData: string,
    iv: string,
    sessionKey: string,
  ): any {
    try {
      // 开发模式：返回模拟数据
      const isDev = process.env.NODE_ENV === 'development';
      if (isDev) {
        this.logger.log('[DEV] 模拟解密微信手机号');
        return {
          phoneNumber: '13800138000',
          purePhoneNumber: '13800138000',
          countryCode: '86',
        };
      }

      // 生产模式：解密数据
      const sessionKeyBuffer = Buffer.from(sessionKey, 'base64');
      const encryptedDataBuffer = Buffer.from(encryptedData, 'base64');
      const ivBuffer = Buffer.from(iv, 'base64');

      const decipher = crypto.createDecipheriv(
        'aes-128-cbc',
        sessionKeyBuffer,
        ivBuffer,
      );
      decipher.setAutoPadding(true);

      let decrypted = decipher.update(encryptedDataBuffer, undefined, 'utf8');
      decrypted += decipher.final('utf8');

      const result = JSON.parse(decrypted);
      return result;
    } catch (error) {
      this.logger.error('解密微信数据失败', error);
      throw new BadRequestException('解密失败');
    }
  }

  private async validateByPhone(phone: string, code: string): Promise<User> {
    const isDev = process.env.NODE_ENV === 'development';
    const devCode = '123456';

    if (isDev && code === devCode) {
      this.logger.log(`[DEV] 开发模式通用验证码登录: ${phone}`);
      let user = await this.userRepo.findOne({ where: { phone } });
      if (!user) {
        // 开发模式自动注册
        user = this.userRepo.create({
          phone,
          nickname: `用户${phone.slice(-4)}`,
        });
        await this.userRepo.save(user);
        this.logger.log(`[DEV] 自动注册用户: ${phone}`);

        // 自动创建用户账户
        await this.createUserAccount(user.id);

        // 生成用户的邀请码
        await this.createUserInviteCode(user.id);
      }
      return user;
    }

    const cachedCode = this.getCachedCode(phone);
    if (!cachedCode || cachedCode !== code) {
      throw new UnauthorizedException('验证码错误或已过期');
    }

    const user = await this.userRepo.findOne({ where: { phone } });
    if (!user) {
      throw new UnauthorizedException('该手机号未注册');
    }

    this.clearCachedCode(phone);
    return user;
  }

  private async validateByPassword(
    phone: string,
    password: string,
  ): Promise<User> {
    const user = await this.userRepo
      .createQueryBuilder('user')
      .where('user.phone = :phone', { phone })
      .addSelect('user.password')
      .getOne();

    if (!user || !user.password) {
      throw new UnauthorizedException('手机号或密码错误');
    }

    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) {
      throw new UnauthorizedException('手机号或密码错误');
    }

    return user;
  }

  private async generateToken(user: User) {
    const payload = { sub: user.id, phone: user.phone };
    const accessToken = await this.jwtService.signAsync(payload);
    const refreshToken = await this.jwtService.signAsync(payload, {
      expiresIn: Number(
        this.configService
          .get<string>('jwt.refreshExpiresIn')
          ?.replace(/[^0-9]/g, '') || '604800',
      ),
    });

    return {
      accessToken,
      refreshToken,
      userInfo: {
        id: user.id,
        phone: user.phone,
        nickname: user.nickname,
        avatar: user.avatar,
        status: user.status,
      },
    };
  }

  private cacheCode(phone: string, code: string): void {
    // TODO: 存入Redis，设置5分钟过期
    this.logger.debug(`缓存验证码: ${phone} -> ${code}`);
  }

  private getCachedCode(phone: string): string | null {
    // TODO: 从Redis获取
    return phone === 'dev' ? '123456' : null; // 开发模式返回固定验证码
  }

  private clearCachedCode(phone: string): void {
    // TODO: 从Redis删除
    this.logger.debug(`清除验证码: ${phone}`);
  }

  private async createUserAccount(userId: number): Promise<void> {
    try {
      const existingAccount = await this.userAccountRepo.findOne({
        where: { user_id: userId },
      });

      if (!existingAccount) {
        const account = this.userAccountRepo.create({
          user_id: userId,
          balance: 0,
          frozen_balance: 0,
          total_income: 0,
          total_expense: 0,
          status: 'active',
        });
        await this.userAccountRepo.save(account);
        this.logger.log(`用户账户创建成功: userId=${userId}`);
      }
    } catch (error) {
      this.logger.error(`创建用户账户失败: userId=${userId}`, error);
    }
  }

  private async handleInviteCode(inviteeId: number, inviteCode: string): Promise<void> {
    try {
      // 检查用户是否已经被邀请过
      const existingInvitation = await this.invitationRepo.findOne({
        where: { inviteeId },
      });

      if (existingInvitation) {
        this.logger.log(`用户已被邀请过，跳过绑定: inviteeId=${inviteeId}`);
        return;
      }

      // 查找邀请人的统计记录
      const inviterStats = await this.inviteStatsRepo.findOne({
        where: { inviteCode },
      });

      if (!inviterStats) {
        this.logger.warn(`邀请码不存在: ${inviteCode}`);
        return;
      }

      // 不能邀请自己
      if (inviterStats.userId === inviteeId) {
        this.logger.warn(`用户尝试使用自己的邀请码: userId=${inviteeId}`);
        return;
      }

      // 创建邀请关系
      const invitation = this.invitationRepo.create({
        activityId: inviterStats.activityId,
        inviterId: inviterStats.userId,
        inviteeId,
        inviteCode,
      });
      await this.invitationRepo.save(invitation);

      // 更新邀请人的统计数据
      inviterStats.totalInvites += 1;
      await this.inviteStatsRepo.save(inviterStats);

      this.logger.log(
        `邀请关系绑定成功: inviter=${inviterStats.userId}, invitee=${inviteeId}, code=${inviteCode}`,
      );
    } catch (error) {
      this.logger.error(`处理邀请码失败: inviteeId=${inviteeId}, code=${inviteCode}`, error);
    }
  }

  private async createUserInviteCode(userId: number): Promise<void> {
    try {
      // 查找启用的邀请返现活动
      const activity = await this.activityRepo
        .createQueryBuilder('activity')
        .where('activity.type = :type', { type: 'invite_cashback' })
        .andWhere('activity.enabled = :enabled', { enabled: true })
        .orderBy('activity.id', 'DESC')
        .getOne();

      if (!activity) {
        this.logger.warn('没有启用的邀请返现活动，跳过生成邀请码');
        return;
      }

      // 生成6位邀请码（0-9, A-Z）
      const inviteCode = this.generateInviteCode();

      // 检查邀请码是否已存在
      const existing = await this.inviteStatsRepo.findOne({
        where: { inviteCode },
      });

      if (existing) {
        // 如果重复，递归重新生成
        this.logger.warn(`邀请码重复: ${inviteCode}，重新生成`);
        return this.createUserInviteCode(userId);
      }

      // 创建用户邀请统计记录
      const inviteStats = this.inviteStatsRepo.create({
        activityId: activity.id,
        userId,
        inviteCode,
        totalInvites: 0,
        totalOrders: 0,
        totalCashback: 0,
        availableBalance: 0,
        withdrawnAmount: 0,
      });
      await this.inviteStatsRepo.save(inviteStats);

      this.logger.log(`用户邀请码生成成功: userId=${userId}, code=${inviteCode}`);
    } catch (error) {
      this.logger.error(`生成用户邀请码失败: userId=${userId}`, error);
    }
  }

  private generateInviteCode(): string {
    const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    let code = '';
    for (let i = 0; i < 6; i++) {
      code += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    return code;
  }
}
