import {
  Injectable,
  NotFoundException,
  BadRequestException,
  Logger,
} from '@nestjs/common';
import { InjectQueue } from '@nestjs/bullmq';
import { Queue } from 'bullmq';
import { PrismaService } from '../../prisma/prisma.service';
import { UpdateIntegrationConfigDto } from './dto/update-integration-config.dto';
import { FindSyncLogsDto } from './dto/find-sync-logs.dto';
import { TriggerSyncDto } from './dto/trigger-sync.dto';
import { Prisma } from '@prisma/client';
import { SyncJobData } from './integration-sync.processor';

export interface PaginatedResult<T> {
  data: T[];
  nextCursor: string | null;
  hasMore: boolean;
}

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

  constructor(
    private readonly prisma: PrismaService,
    @InjectQueue('integration-sync') private readonly syncQueue: Queue,
  ) {}

  // ─── Config ──────────────────────────────────────────────────────

  async getConfig(clubId: string) {
    const config = await this.prisma.integrationConfig.findUnique({
      where: { clubId },
    });

    if (!config) {
      // Return a default empty config rather than 404
      return {
        clubId,
        provider: '1c',
        apiUrl: null,
        apiKey: null,
        syncEnabled: false,
        lastSyncAt: null,
        settings: null,
      };
    }

    // Mask API key for security
    return {
      ...config,
      apiKey: config.apiKey ? '***' + config.apiKey.slice(-4) : null,
    };
  }

  async upsertConfig(clubId: string, dto: UpdateIntegrationConfigDto) {
    const existing = await this.prisma.integrationConfig.findUnique({
      where: { clubId },
    });

    if (existing) {
      return this.prisma.integrationConfig.update({
        where: { clubId },
        data: {
          ...(dto.apiUrl !== undefined ? { apiUrl: dto.apiUrl } : {}),
          ...(dto.apiKey !== undefined ? { apiKey: dto.apiKey } : {}),
          ...(dto.syncEnabled !== undefined
            ? { syncEnabled: dto.syncEnabled }
            : {}),
          ...(dto.settings !== undefined ? { settings: dto.settings } : {}),
        },
      });
    }

    return this.prisma.integrationConfig.create({
      data: {
        clubId,
        provider: '1c',
        apiUrl: dto.apiUrl,
        apiKey: dto.apiKey,
        syncEnabled: dto.syncEnabled ?? false,
        settings: dto.settings ?? {},
      },
    });
  }

  // ─── Sync ────────────────────────────────────────────────────────

  async triggerSync(clubId: string, dto: TriggerSyncDto) {
    const config = await this.prisma.integrationConfig.findUnique({
      where: { clubId },
    });

    if (!config || !config.syncEnabled) {
      throw new BadRequestException(
        'Integration sync is not enabled. Configure and enable sync first.',
      );
    }

    if (!config.apiUrl || !config.apiKey) {
      throw new BadRequestException(
        'Integration API URL and API key must be configured before sync.',
      );
    }

    // Create a sync log entry with status "in progress"
    const syncLog = await this.prisma.syncLog.create({
      data: {
        clubId,
        integrationId: config.id,
        direction: dto.direction,
        entityType: dto.entityType,
        recordsProcessed: 0,
        recordsFailed: 0,
        startedAt: new Date(),
      },
    });

    // Dispatch to BullMQ queue for async processing
    const jobData: SyncJobData = {
      clubId,
      syncLogId: syncLog.id,
      integrationId: config.id,
      direction: dto.direction as 'inbound' | 'outbound',
      entityType: dto.entityType,
      apiUrl: config.apiUrl!,
      apiKey: config.apiKey!,
    };

    await this.syncQueue.add(`sync-${dto.entityType}`, jobData, {
      attempts: 3,
      backoff: { type: 'exponential', delay: 5000 },
      removeOnComplete: 100,
      removeOnFail: 50,
    });

    this.logger.log(
      `Sync job queued: ${dto.direction} ${dto.entityType} for club ${clubId}`,
    );

    return {
      syncLogId: syncLog.id,
      status: 'started',
      message: `Sync ${dto.direction} for ${dto.entityType} has been queued.`,
    };
  }

  async findSyncLogs(
    clubId: string,
    params: FindSyncLogsDto,
  ): Promise<PaginatedResult<any>> {
    const { cursor, limit = 20, direction, entityType } = params;

    const where: Prisma.SyncLogWhereInput = {
      clubId,
      ...(direction ? { direction } : {}),
      ...(entityType ? { entityType } : {}),
    };

    const items = await this.prisma.syncLog.findMany({
      where,
      take: limit + 1,
      ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}),
      orderBy: { createdAt: 'desc' },
    });

    const hasMore = items.length > limit;
    const data = hasMore ? items.slice(0, -1) : items;

    return {
      data,
      nextCursor: hasMore ? data[data.length - 1].id : null,
      hasMore,
    };
  }
}
