import { randomUUID } from 'node:crypto';

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

import type {
  WhmcsChangePackageRequest,
  WhmcsPlanCatalogResponse,
  WhmcsProvisionServiceRequest,
  WhmcsServiceResponse,
  WhmcsUpsertClientRequest,
  WhmcsUpsertClientResponse,
  WhmcsValidatePlanResponse,
} from '@hexahost/contracts';
import { formatJoinAddress } from '@hexahost/dns';
import type { GameServer } from '@hexahost/database';

import { BillingService } from '../../billing/billing.service';
import { getBillingProvider } from '../../billing/billing-provider';
import { PrismaService } from '../../prisma/prisma.service';
import { ServersService } from '../../servers/servers.service';

import type { WhmcsInstallationContext } from './whmcs-integration.guard';

type InstallationRef = {
  id: string;
  integrationId: string;
};

@Injectable()
export class WhmcsIntegrationService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly serversService: ServersService,
    private readonly billing: BillingService,
  ) {}

  getHealth(installation: WhmcsInstallationContext) {
    return {
      status: 'ok' as const,
      integrationId: installation.integrationId,
      billingProvider: getBillingProvider(),
    };
  }

  async listPlans(): Promise<WhmcsPlanCatalogResponse> {
    const plans = await this.prisma.plan.findMany({
      where: { isActive: true },
      orderBy: { name: 'asc' },
    });

    return {
      plans: plans.map((plan) => ({
        id: plan.id,
        slug: plan.slug,
        name: plan.name,
        maxRamMb: plan.maxRamMb,
        isFree: plan.isFree,
      })),
    };
  }

  async validatePlanSlug(planSlug: string): Promise<WhmcsValidatePlanResponse> {
    const plan = await this.prisma.plan.findFirst({
      where: { slug: planSlug, isActive: true },
    });

    if (!plan) {
      return { valid: false, planSlug, plan: null };
    }

    return {
      valid: true,
      planSlug,
      plan: {
        id: plan.id,
        slug: plan.slug,
        name: plan.name,
        maxRamMb: plan.maxRamMb,
        isFree: plan.isFree,
      },
    };
  }

  async upsertClient(
    installation: InstallationRef,
    externalClientId: string,
    input: WhmcsUpsertClientRequest,
    idempotencyKey: string,
  ): Promise<WhmcsUpsertClientResponse> {
    const existingOp = await this.findCompletedOperation(installation.id, idempotencyKey);
    if (existingOp?.result) {
      return existingOp.result as WhmcsUpsertClientResponse;
    }

    const existingLink = await this.prisma.whmcsClientLink.findUnique({
      where: {
        installationId_externalClientId: {
          installationId: installation.id,
          externalClientId,
        },
      },
    });

    if (existingLink) {
      const user = await this.prisma.user.update({
        where: { id: existingLink.userId },
        data: {
          displayName: input.displayName,
        },
      });

      const result = {
        userId: user.id,
        externalClientId,
        created: false,
      };

      await this.recordOperation(installation.id, idempotencyKey, 'client:upsert', result);
      return result;
    }

    const user = await this.prisma.user.create({
      data: {
        email: input.email,
        username: input.username,
        displayName: input.displayName,
        status: 'ACTIVE',
      },
    });

    await this.billing.ensureWalletForUser(user.id);

    await this.prisma.whmcsClientLink.create({
      data: {
        installationId: installation.id,
        externalClientId,
        userId: user.id,
      },
    });

    const result = {
      userId: user.id,
      externalClientId,
      created: true,
    };

    await this.recordOperation(installation.id, idempotencyKey, 'client:upsert', result);
    return result;
  }

  async provisionService(
    installation: InstallationRef,
    input: WhmcsProvisionServiceRequest,
    idempotencyKey: string,
  ): Promise<WhmcsServiceResponse> {
    const existingOp = await this.findCompletedOperation(installation.id, idempotencyKey);
    if (existingOp?.result) {
      return existingOp.result as WhmcsServiceResponse;
    }

    const existing = await this.prisma.whmcsServiceLink.findUnique({
      where: {
        installationId_externalServiceId: {
          installationId: installation.id,
          externalServiceId: input.externalServiceId,
        },
      },
      include: { server: true },
    });

    if (existing) {
      return this.toServiceResponse(existing.externalServiceId, existing.status, existing.server);
    }

    const clientLink = await this.prisma.whmcsClientLink.findUnique({
      where: {
        installationId_externalClientId: {
          installationId: installation.id,
          externalClientId: input.externalClientId,
        },
      },
    });

    if (!clientLink) {
      throw new NotFoundException(`Unknown WHMCS client ${input.externalClientId}`);
    }

    const plan = await this.prisma.plan.findFirst({
      where: { slug: input.planSlug, isActive: true },
    });

    if (!plan) {
      throw new NotFoundException(`Unknown plan slug ${input.planSlug}`);
    }

    await this.billing.assertCanCreateServer(clientLink.userId, plan.id);

    const server = await this.serversService.createServer(clientLink.userId, {
      name: input.serverName,
      edition: input.edition,
      softwareFamily: input.softwareFamily,
      minecraftVersion: input.minecraftVersion,
      eulaAccepted: true,
      planId: plan.id,
      ramMb: plan.maxRamMb,
    });

    await this.prisma.whmcsServiceLink.create({
      data: {
        installationId: installation.id,
        externalServiceId: input.externalServiceId,
        externalProductId: input.externalProductId,
        serverId: server.id,
        status: 'PENDING',
      },
    });

    const started = await this.serversService.startServerInternal(
      server.id,
      `whmcs:provision:${input.externalServiceId}`,
    );

    await this.prisma.whmcsServiceLink.update({
      where: {
        installationId_externalServiceId: {
          installationId: installation.id,
          externalServiceId: input.externalServiceId,
        },
      },
      data: { status: 'ACTIVE' },
    });

    const response = this.toServiceResponse(
      input.externalServiceId,
      'ACTIVE',
      started.server as unknown as GameServer,
    );

    await this.recordOperation(
      installation.id,
      idempotencyKey,
      'service:provision',
      response,
      input.externalServiceId,
    );

    return response;
  }

  async getService(
    installation: InstallationRef,
    externalServiceId: string,
  ): Promise<WhmcsServiceResponse> {
    const link = await this.findServiceLink(installation.id, externalServiceId);
    return this.toServiceResponse(externalServiceId, link.status, link.server);
  }

  async suspendService(
    installation: InstallationRef,
    externalServiceId: string,
    reason: string | undefined,
    idempotencyKey: string,
  ): Promise<WhmcsServiceResponse> {
    const existingOp = await this.findCompletedOperation(installation.id, idempotencyKey);
    if (existingOp?.result) {
      return existingOp.result as WhmcsServiceResponse;
    }

    const link = await this.findServiceLink(installation.id, externalServiceId);
    const server = link.server;

    if (server.status === 'RUNNING') {
      await this.serversService.stopServer(server.userId, server.id);
    } else if (server.status === 'QUEUED') {
      await this.serversService.cancelQueuedStart(server.userId, server.id);
    }

    const updatedServer = await this.prisma.gameServer.update({
      where: { id: server.id },
      data: {
        billingSuspendedAt: new Date(),
        billingSuspendReason: reason ?? 'whmcs-suspend',
      },
    });

    const updatedLink = await this.prisma.whmcsServiceLink.update({
      where: { id: link.id },
      data: {
        status: 'SUSPENDED',
        suspendedAt: new Date(),
      },
    });

    const response = this.toServiceResponse(
      externalServiceId,
      updatedLink.status,
      updatedServer,
    );

    await this.recordOperation(
      installation.id,
      idempotencyKey,
      'service:suspend',
      response,
      externalServiceId,
    );

    return response;
  }

  async unsuspendService(
    installation: InstallationRef,
    externalServiceId: string,
    idempotencyKey: string,
  ): Promise<WhmcsServiceResponse> {
    const existingOp = await this.findCompletedOperation(installation.id, idempotencyKey);
    if (existingOp?.result) {
      return existingOp.result as WhmcsServiceResponse;
    }

    const link = await this.findServiceLink(installation.id, externalServiceId);

    const updatedServer = await this.prisma.gameServer.update({
      where: { id: link.serverId },
      data: {
        billingSuspendedAt: null,
        billingSuspendReason: null,
      },
    });

    const updatedLink = await this.prisma.whmcsServiceLink.update({
      where: { id: link.id },
      data: {
        status: 'ACTIVE',
        suspendedAt: null,
      },
    });

    const response = this.toServiceResponse(
      externalServiceId,
      updatedLink.status,
      updatedServer,
    );

    await this.recordOperation(
      installation.id,
      idempotencyKey,
      'service:unsuspend',
      response,
      externalServiceId,
    );

    return response;
  }

  async terminateService(
    installation: InstallationRef,
    externalServiceId: string,
    idempotencyKey: string,
  ): Promise<WhmcsServiceResponse> {
    const existingOp = await this.findCompletedOperation(installation.id, idempotencyKey);
    if (existingOp?.result) {
      return existingOp.result as WhmcsServiceResponse;
    }

    const link = await this.findServiceLink(installation.id, externalServiceId);
    const server = link.server;

    if (server.status === 'RUNNING') {
      await this.serversService.stopServer(server.userId, server.id);
    }

    const updatedServer = await this.prisma.gameServer.update({
      where: { id: server.id },
      data: {
        status: 'DELETED',
        billingSuspendedAt: new Date(),
        billingSuspendReason: 'whmcs-terminate',
      },
    });

    const updatedLink = await this.prisma.whmcsServiceLink.update({
      where: { id: link.id },
      data: {
        status: 'TERMINATED',
        terminatedAt: new Date(),
      },
    });

    const response = this.toServiceResponse(
      externalServiceId,
      updatedLink.status,
      updatedServer,
    );

    await this.recordOperation(
      installation.id,
      idempotencyKey,
      'service:terminate',
      response,
      externalServiceId,
    );

    return response;
  }

  async changePackage(
    installation: InstallationRef,
    externalServiceId: string,
    input: WhmcsChangePackageRequest,
    idempotencyKey: string,
  ): Promise<WhmcsServiceResponse> {
    const existingOp = await this.findCompletedOperation(installation.id, idempotencyKey);
    if (existingOp?.result) {
      return existingOp.result as WhmcsServiceResponse;
    }

    const link = await this.findServiceLink(installation.id, externalServiceId);
    const plan = await this.prisma.plan.findFirst({
      where: { slug: input.planSlug, isActive: true },
    });

    if (!plan) {
      throw new NotFoundException(`Unknown plan slug ${input.planSlug}`);
    }

    const updatedServer = await this.prisma.gameServer.update({
      where: { id: link.serverId },
      data: {
        planId: plan.id,
        ramMb: input.ramMb ?? plan.maxRamMb,
      },
    });

    const response = this.toServiceResponse(
      externalServiceId,
      link.status,
      updatedServer,
    );

    await this.recordOperation(
      installation.id,
      idempotencyKey,
      'service:change-package',
      response,
      externalServiceId,
    );

    return response;
  }

  private async findServiceLink(installationId: string, externalServiceId: string) {
    const link = await this.prisma.whmcsServiceLink.findUnique({
      where: {
        installationId_externalServiceId: {
          installationId,
          externalServiceId,
        },
      },
      include: { server: true },
    });

    if (!link) {
      throw new NotFoundException(`Unknown WHMCS service ${externalServiceId}`);
    }

    return link;
  }

  private async findCompletedOperation(installationId: string, idempotencyKey: string) {
    return this.prisma.integrationOperation.findUnique({
      where: { idempotencyKey },
    });
  }

  private async recordOperation(
    installationId: string,
    idempotencyKey: string,
    operation: string,
    result: unknown,
    externalRef?: string,
  ): Promise<void> {
    await this.prisma.integrationOperation.create({
      data: {
        installationId,
        idempotencyKey,
        operation,
        externalRef,
        status: 'COMPLETED',
        result: result as object,
      },
    });
  }

  private toServiceResponse(
    externalServiceId: string,
    status: 'PENDING' | 'ACTIVE' | 'SUSPENDED' | 'TERMINATED',
    server: GameServer,
  ): WhmcsServiceResponse {
    return {
      externalServiceId,
      status,
      server: {
        id: server.id,
        userId: server.userId,
        planId: server.planId,
        nodeId: server.nodeId,
        name: server.name,
        status: server.status,
        version: server.version,
        edition: server.edition,
        softwareFamily: server.softwareFamily,
        minecraftVersion: server.minecraftVersion,
        eulaAccepted: server.eulaAccepted,
        hostPort: server.hostPort,
        containerId: server.containerId,
        dataPath: server.dataPath,
        activeWorldName: server.activeWorldName,
        ramMb: server.ramMb,
        idleShutdownEnabled: server.idleShutdownEnabled,
        idleStopAt: server.idleStopAt?.toISOString() ?? null,
        joinSlug: server.joinSlug,
        joinHostname: formatJoinAddress(server.joinSlug, server.edition),
        joinToStartEnabled: server.joinToStartEnabled,
        createdAt: server.createdAt.toISOString(),
        updatedAt: server.updatedAt.toISOString(),
      },
    };
  }
}
