import { ConflictException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { FinancialOperation } from '../entities/financial-operation.entity';

@Injectable()
export class FinancialOperationsService {
  constructor(
    @InjectRepository(FinancialOperation)
    private readonly repo: Repository<FinancialOperation>,
  ) {}

  async run<T>(operationId: string | undefined, type: string, work: () => Promise<T>): Promise<T> {
    if (!operationId) return work();
    if (!/^[\w-]{8,64}$/.test(operationId)) throw new ConflictException('operationId 格式无效');

    const existing = await this.repo.findOne({ where: { operationId } });
    if (existing) {
      if (existing.type !== type) throw new ConflictException('operationId 已用于其他操作');
      if (existing.status === 'completed' && existing.resultJson) return JSON.parse(existing.resultJson) as T;
      if (existing.status === 'running') throw new ConflictException('该操作正在处理中，请勿重复提交');
    }

    let operation = existing;
    if (!operation) {
      try {
        operation = await this.repo.save(this.repo.create({ operationId, type, status: 'running' }));
      } catch (error) {
        const concurrent = await this.repo.findOne({ where: { operationId } });
        if (concurrent?.status === 'completed' && concurrent.resultJson) {
          return JSON.parse(concurrent.resultJson) as T;
        }
        throw new ConflictException('该操作正在处理中，请勿重复提交', { cause: error });
      }
    } else {
      operation.status = 'running';
      operation.errorMessage = null;
      operation.resultJson = null;
      await this.repo.save(operation);
    }

    try {
      const result = await work();
      operation.status = 'completed';
      operation.resultJson = JSON.stringify(result);
      await this.repo.save(operation);
      return result;
    } catch (error) {
      operation.status = 'failed';
      operation.errorMessage = error instanceof Error ? error.message.slice(0, 500) : '未知错误';
      await this.repo.save(operation);
      throw error;
    }
  }
}
