import { Injectable, Logger } from '@nestjs/common';
import { SystemAccountRepository } from '../../infrastructure/persistence/repositories/system-account.repository';
import { RegionRepository } from '../../infrastructure/persistence/repositories/region.repository';
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
import { SystemAccount, SystemAccountType, AssetType, TransactionType, CounterpartyType } from '@prisma/client';
import Decimal from 'decimal.js';
import { DomainException } from '../../shared/filters/domain-exception.filter';

export interface InitializeSystemAccountsInput {
  headquarters: { name: string; code: string };
  operation: { name: string; code: string };
  fee: { name: string; code: string };
}

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

  constructor(
    private readonly systemAccountRepo: SystemAccountRepository,
    private readonly regionRepo: RegionRepository,
    private readonly outboxRepo: OutboxRepository,
  ) {}

  /**
   * 初始化核心系统账户（总部、运营、手续费）
   */
  async initializeCoreAccounts(input: InitializeSystemAccountsInput): Promise<SystemAccount[]> {
    const accounts: SystemAccount[] = [];

    // 总部账户
    const hq = await this.systemAccountRepo.create({
      accountType: 'HEADQUARTERS',
      name: input.headquarters.name,
      code: input.headquarters.code,
    });
    accounts.push(hq);

    // 运营账户
    const op = await this.systemAccountRepo.create({
      accountType: 'OPERATION',
      name: input.operation.name,
      code: input.operation.code,
    });
    accounts.push(op);

    // 手续费账户
    const fee = await this.systemAccountRepo.create({
      accountType: 'FEE',
      name: input.fee.name,
      code: input.fee.code,
    });
    accounts.push(fee);

    // 发布初始化事件
    await this.outboxRepo.create({
      aggregateType: 'SystemAccount',
      aggregateId: 'CORE_ACCOUNTS',
      eventType: 'CORE_ACCOUNTS_INITIALIZED',
      payload: {
        accounts: accounts.map((a) => ({
          id: a.id,
          type: a.accountType,
          code: a.code,
        })),
        initializedAt: new Date().toISOString(),
      },
    });

    this.logger.log(`Core system accounts initialized: ${accounts.length} accounts`);
    return accounts;
  }

  /**
   * 创建省级公司账户
   */
  async createProvinceAccount(
    provinceId: string,
    name: string,
    code: string,
  ): Promise<SystemAccount> {
    const province = await this.regionRepo.findProvinceById(provinceId);
    if (!province) {
      throw new DomainException(`Province not found: ${provinceId}`, 'PROVINCE_NOT_FOUND');
    }

    const account = await this.systemAccountRepo.create({
      accountType: 'PROVINCE',
      name,
      code,
      provinceId,
    });

    await this.outboxRepo.create({
      aggregateType: 'SystemAccount',
      aggregateId: account.id,
      eventType: 'PROVINCE_ACCOUNT_CREATED',
      payload: {
        accountId: account.id,
        provinceId,
        provinceName: province.name,
        code,
      },
    });

    this.logger.log(`Province account created: ${code} for ${province.name}`);
    return account;
  }

  /**
   * 创建市级公司账户
   */
  async createCityAccount(
    cityId: string,
    name: string,
    code: string,
  ): Promise<SystemAccount> {
    const city = await this.regionRepo.findCityById(cityId);
    if (!city) {
      throw new DomainException(`City not found: ${cityId}`, 'CITY_NOT_FOUND');
    }

    const account = await this.systemAccountRepo.create({
      accountType: 'CITY',
      name,
      code,
      provinceId: city.provinceId,
      cityId,
    });

    await this.outboxRepo.create({
      aggregateType: 'SystemAccount',
      aggregateId: account.id,
      eventType: 'CITY_ACCOUNT_CREATED',
      payload: {
        accountId: account.id,
        cityId,
        cityName: city.name,
        provinceId: city.provinceId,
        code,
      },
    });

    this.logger.log(`City account created: ${code} for ${city.name}`);
    return account;
  }

  /**
   * 收取手续费（从用户到手续费账户）
   */
  async collectFee(
    assetType: AssetType,
    amount: Decimal,
    fromAccountSeq: string,
    fromUserId: string,
    referenceInfo: {
      referenceId: string;
      referenceType: string;
      memo: string;
    },
  ): Promise<void> {
    const feeAccounts = await this.systemAccountRepo.findByType('FEE');
    if (feeAccounts.length === 0) {
      throw new DomainException('Fee account not found', 'FEE_ACCOUNT_NOT_FOUND');
    }

    const feeAccount = feeAccounts[0];

    await this.systemAccountRepo.updateBalanceWithTransaction(
      feeAccount.id,
      assetType,
      amount,
      {
        transactionType: 'FEE_COLLECT',
        counterpartyType: 'USER',
        counterpartyAccountSeq: fromAccountSeq,
        counterpartyUserId: fromUserId,
        referenceId: referenceInfo.referenceId,
        referenceType: referenceInfo.referenceType,
        memo: referenceInfo.memo || `收取手续费自用户[${fromAccountSeq}], 数量${amount.toFixed(8)}`,
      },
    );

    await this.outboxRepo.create({
      aggregateType: 'SystemAccount',
      aggregateId: feeAccount.id,
      eventType: 'FEE_COLLECTED',
      payload: {
        feeAccountId: feeAccount.id,
        assetType,
        amount: amount.toString(),
        fromAccountSeq,
        fromUserId,
        referenceId: referenceInfo.referenceId,
      },
    });
  }

  /**
   * 分发手续费到各级账户
   */
  async distributeFee(
    assetType: AssetType,
    totalAmount: Decimal,
    distribution: {
      headquartersRate: Decimal;
      operationRate: Decimal;
      provinceId?: string;
      provinceRate?: Decimal;
      cityId?: string;
      cityRate?: Decimal;
    },
    referenceInfo: {
      referenceId: string;
      referenceType: string;
    },
  ): Promise<void> {
    const feeAccounts = await this.systemAccountRepo.findByType('FEE');
    if (feeAccounts.length === 0) {
      throw new DomainException('Fee account not found', 'FEE_ACCOUNT_NOT_FOUND');
    }

    const feeAccount = feeAccounts[0];

    // 计算各账户分成
    const hqAmount = totalAmount.mul(distribution.headquartersRate);
    const opAmount = totalAmount.mul(distribution.operationRate);
    let provinceAmount = new Decimal(0);
    let cityAmount = new Decimal(0);

    if (distribution.provinceRate && distribution.provinceId) {
      provinceAmount = totalAmount.mul(distribution.provinceRate);
    }
    if (distribution.cityRate && distribution.cityId) {
      cityAmount = totalAmount.mul(distribution.cityRate);
    }

    const totalDistributed = hqAmount.plus(opAmount).plus(provinceAmount).plus(cityAmount);

    // 从手续费账户扣除
    await this.systemAccountRepo.updateBalanceWithTransaction(
      feeAccount.id,
      assetType,
      totalDistributed.negated(),
      {
        transactionType: 'FEE_DISTRIBUTE',
        counterpartyType: 'SYSTEM_ACCOUNT',
        referenceId: referenceInfo.referenceId,
        referenceType: referenceInfo.referenceType,
        memo: `分发手续费, 总额${totalDistributed.toFixed(8)}`,
      },
    );

    // 分发到总部
    const hqAccounts = await this.systemAccountRepo.findByType('HEADQUARTERS');
    if (hqAccounts.length > 0 && hqAmount.greaterThan(0)) {
      await this.systemAccountRepo.updateBalanceWithTransaction(
        hqAccounts[0].id,
        assetType,
        hqAmount,
        {
          transactionType: 'FEE_DISTRIBUTE',
          counterpartyType: 'SYSTEM_ACCOUNT',
          counterpartySystemId: feeAccount.id,
          referenceId: referenceInfo.referenceId,
          referenceType: referenceInfo.referenceType,
          memo: `收到手续费分成, 数量${hqAmount.toFixed(8)}`,
        },
      );
    }

    // 分发到运营
    const opAccounts = await this.systemAccountRepo.findByType('OPERATION');
    if (opAccounts.length > 0 && opAmount.greaterThan(0)) {
      await this.systemAccountRepo.updateBalanceWithTransaction(
        opAccounts[0].id,
        assetType,
        opAmount,
        {
          transactionType: 'FEE_DISTRIBUTE',
          counterpartyType: 'SYSTEM_ACCOUNT',
          counterpartySystemId: feeAccount.id,
          referenceId: referenceInfo.referenceId,
          referenceType: referenceInfo.referenceType,
          memo: `收到手续费分成, 数量${opAmount.toFixed(8)}`,
        },
      );
    }

    // 分发到省级
    if (distribution.provinceId && provinceAmount.greaterThan(0)) {
      const provinceAccounts = await this.systemAccountRepo.findByRegion(distribution.provinceId);
      const provinceAccount = provinceAccounts.find((a) => a.accountType === 'PROVINCE');
      if (provinceAccount) {
        await this.systemAccountRepo.updateBalanceWithTransaction(
          provinceAccount.id,
          assetType,
          provinceAmount,
          {
            transactionType: 'FEE_DISTRIBUTE',
            counterpartyType: 'SYSTEM_ACCOUNT',
            counterpartySystemId: feeAccount.id,
            referenceId: referenceInfo.referenceId,
            referenceType: referenceInfo.referenceType,
            memo: `收到手续费分成, 数量${provinceAmount.toFixed(8)}`,
          },
        );
      }
    }

    // 分发到市级
    if (distribution.cityId && cityAmount.greaterThan(0)) {
      const cityAccounts = await this.systemAccountRepo.findByRegion(undefined, distribution.cityId);
      const cityAccount = cityAccounts.find((a) => a.accountType === 'CITY');
      if (cityAccount) {
        await this.systemAccountRepo.updateBalanceWithTransaction(
          cityAccount.id,
          assetType,
          cityAmount,
          {
            transactionType: 'FEE_DISTRIBUTE',
            counterpartyType: 'SYSTEM_ACCOUNT',
            counterpartySystemId: feeAccount.id,
            referenceId: referenceInfo.referenceId,
            referenceType: referenceInfo.referenceType,
            memo: `收到手续费分成, 数量${cityAmount.toFixed(8)}`,
          },
        );
      }
    }

    await this.outboxRepo.create({
      aggregateType: 'SystemAccount',
      aggregateId: feeAccount.id,
      eventType: 'FEE_DISTRIBUTED',
      payload: {
        totalAmount: totalAmount.toString(),
        distribution: {
          headquarters: hqAmount.toString(),
          operation: opAmount.toString(),
          province: provinceAmount.toString(),
          city: cityAmount.toString(),
        },
        referenceId: referenceInfo.referenceId,
      },
    });
  }

  async findByCode(code: string): Promise<SystemAccount | null> {
    return this.systemAccountRepo.findByCode(code);
  }

  async findByType(accountType: SystemAccountType): Promise<SystemAccount[]> {
    return this.systemAccountRepo.findByType(accountType);
  }

  async findAll(): Promise<SystemAccount[]> {
    return this.systemAccountRepo.findAll();
  }
}
