import { Injectable } from '@nestjs/common';

interface MockBackupShare {
  shareId: bigint;
  userId: bigint;
  accountSequence: bigint;
  publicKey: string;
  partyIndex: number;
  threshold: number;
  totalParties: number;
  encryptedShareData: string;
  encryptionKeyId: string;
  status: string;
  accessCount: number;
  lastAccessedAt: Date | null;
  createdAt: Date;
  updatedAt: Date;
  revokedAt: Date | null;
}

interface MockShareAccessLog {
  logId: bigint;
  shareId: bigint;
  userId: bigint;
  action: string;
  sourceService: string;
  sourceIp: string;
  success: boolean;
  errorMessage: string | null;
  createdAt: Date;
}

@Injectable()
export class MockPrismaService {
  private backupShares: MockBackupShare[] = [];
  private shareAccessLogs: MockShareAccessLog[] = [];
  private shareIdCounter = 1n;
  private logIdCounter = 1n;

  backupShare = {
    create: jest.fn().mockImplementation(({ data }) => {
      const share: MockBackupShare = {
        shareId: this.shareIdCounter++,
        userId: data.userId,
        accountSequence: data.accountSequence,
        publicKey: data.publicKey,
        partyIndex: data.partyIndex ?? 2,
        threshold: data.threshold ?? 2,
        totalParties: data.totalParties ?? 3,
        encryptedShareData: data.encryptedShareData,
        encryptionKeyId: data.encryptionKeyId,
        status: data.status ?? 'ACTIVE',
        accessCount: data.accessCount ?? 0,
        lastAccessedAt: data.lastAccessedAt ?? null,
        createdAt: new Date(),
        updatedAt: new Date(),
        revokedAt: data.revokedAt ?? null,
      };
      this.backupShares.push(share);
      return Promise.resolve(share);
    }),

    update: jest.fn().mockImplementation(({ where, data }) => {
      const index = this.backupShares.findIndex(s => s.shareId === where.shareId);
      if (index === -1) return Promise.resolve(null);

      this.backupShares[index] = {
        ...this.backupShares[index],
        ...data,
        updatedAt: new Date(),
      };
      return Promise.resolve(this.backupShares[index]);
    }),

    findUnique: jest.fn().mockImplementation(({ where }) => {
      let share: MockBackupShare | undefined;
      if (where.shareId) {
        share = this.backupShares.find(s => s.shareId === where.shareId);
      } else if (where.userId) {
        share = this.backupShares.find(s => s.userId === where.userId);
      } else if (where.publicKey) {
        share = this.backupShares.find(s => s.publicKey === where.publicKey);
      } else if (where.accountSequence) {
        share = this.backupShares.find(s => s.accountSequence === where.accountSequence);
      }
      return Promise.resolve(share ?? null);
    }),

    findFirst: jest.fn().mockImplementation(({ where }) => {
      const share = this.backupShares.find(s =>
        s.userId === where.userId && s.publicKey === where.publicKey
      );
      return Promise.resolve(share ?? null);
    }),

    delete: jest.fn().mockImplementation(({ where }) => {
      const index = this.backupShares.findIndex(s => s.shareId === where.shareId);
      if (index === -1) return Promise.resolve(null);
      const deleted = this.backupShares.splice(index, 1)[0];
      return Promise.resolve(deleted);
    }),

    deleteMany: jest.fn().mockImplementation(() => {
      const count = this.backupShares.length;
      this.backupShares = [];
      return Promise.resolve({ count });
    }),
  };

  shareAccessLog = {
    create: jest.fn().mockImplementation(({ data }) => {
      const log: MockShareAccessLog = {
        logId: this.logIdCounter++,
        shareId: data.shareId,
        userId: data.userId,
        action: data.action,
        sourceService: data.sourceService,
        sourceIp: data.sourceIp,
        success: data.success ?? true,
        errorMessage: data.errorMessage ?? null,
        createdAt: new Date(),
      };
      this.shareAccessLogs.push(log);
      return Promise.resolve(log);
    }),

    findMany: jest.fn().mockImplementation(({ where, orderBy, take }) => {
      let logs = [...this.shareAccessLogs];
      if (where?.shareId) {
        logs = logs.filter(l => l.shareId === where.shareId);
      }
      if (where?.userId) {
        logs = logs.filter(l => l.userId === where.userId);
      }
      if (orderBy?.createdAt === 'desc') {
        logs.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
      }
      if (take) {
        logs = logs.slice(0, take);
      }
      return Promise.resolve(logs);
    }),

    count: jest.fn().mockImplementation(({ where }) => {
      let logs = [...this.shareAccessLogs];
      if (where?.userId) {
        logs = logs.filter(l => l.userId === where.userId);
      }
      if (where?.action) {
        logs = logs.filter(l => l.action === where.action);
      }
      if (where?.createdAt?.gte) {
        logs = logs.filter(l => l.createdAt >= where.createdAt.gte);
      }
      return Promise.resolve(logs.length);
    }),

    deleteMany: jest.fn().mockImplementation(() => {
      const count = this.shareAccessLogs.length;
      this.shareAccessLogs = [];
      return Promise.resolve({ count });
    }),
  };

  $connect = jest.fn().mockResolvedValue(undefined);
  $disconnect = jest.fn().mockResolvedValue(undefined);
  $queryRaw = jest.fn().mockResolvedValue([{ '?column?': 1 }]);

  // Helper methods for testing
  reset(): void {
    this.backupShares = [];
    this.shareAccessLogs = [];
    this.shareIdCounter = 1n;
    this.logIdCounter = 1n;
    jest.clearAllMocks();
  }

  getBackupShares(): MockBackupShare[] {
    return [...this.backupShares];
  }

  getShareAccessLogs(): MockShareAccessLog[] {
    return [...this.shareAccessLogs];
  }
}
