import { randomBytes } from "crypto";
import { makeLogger } from "./logger.js";

const logger = makeLogger("provisioner");

export interface ProvisionerConfig {
  doApiToken: string;
  doRegion: string;
  doSize: string;
  doVolumeSizeGb: number;
  doVpcUuid: string; // New: Digital Ocean VPC UUID
  doSshKeyFingerprint: string; // New: Digital Ocean SSH Key Fingerprint
  tailscaleApiKey: string;
  tailscaleTailnet: string;
}

const stubProvisioningResults = new Map<string, any>(); // To store fake results for stub mode

export class ProvisionerService {
  private readonly config: ProvisionerConfig;
  readonly stubMode: boolean;

  constructor(config?: Partial<ProvisionerConfig>) {
    this.config = {
      doApiToken: config?.doApiToken ?? process.env.DO_API_TOKEN ?? "",
      doRegion: config?.doRegion ?? process.env.DO_REGION ?? "nyc3",
      doSize: config?.doSize ?? process.env.DO_SIZE ?? "s-2vcpu-4gb",
      doVolumeSizeGb: config?.doVolumeSizeGb ?? parseInt(process.env.DO_VOLUME_SIZE_GB ?? "100", 10),
      doVpcUuid: config?.doVpcUuid ?? process.env.DO_VPC_UUID ?? "", // New
      doSshKeyFingerprint: config?.doSshKeyFingerprint ?? process.env.DO_SSH_KEY_FINGERPRINT ?? "", // New
      tailscaleApiKey: config?.tailscaleApiKey ?? process.env.TAILSCALE_API_KEY ?? "",
      tailscaleTailnet: config?.tailscaleTailnet ?? process.env.TAILSCALE_TAILNET ?? "",
    };
    this.stubMode = !this.config.doApiToken || !this.config.tailscaleApiKey;
    if (this.stubMode) {
      logger.warn("no DO_API_TOKEN or TAILSCALE_API_KEY — running in STUB mode", { stub: true });
    } else {
      logger.info("Provisioner real mode active", { stub: false });
    }
  }

  async provisionNode(jobId: string): Promise<{
    dropletId: string;
    nodeIp: string;
    tailscaleHostname: string;
    lnbitsUrl: string;
    sshPrivateKey: string;
  }> {
    if (this.stubMode) {
      logger.info("stub provisioning node", { jobId });
      const fakeSshPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY-----
FakeKeyForJob${jobId}
-----END OPENSSH PRIVATE KEY-----`;
      const fakeTailscaleHostname = `fake-node-${jobId.slice(0, 8)}`;
      const fakeNodeIp = `192.168.0.${Math.floor(Math.random() * 255)}`;
      const fakeLnbitsUrl = `http://${fakeNodeIp}:3000/lnbits`;
      const result = {
        dropletId: `fake-droplet-${jobId}`,
        nodeIp: fakeNodeIp,
        tailscaleHostname: fakeTailscaleHostname,
        lnbitsUrl: fakeLnbitsUrl,
        sshPrivateKey: fakeSshPrivateKey,
      };
      stubProvisioningResults.set(jobId, result);
      await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate delay
      return result;
    }

    // Real provisioning logic
    const { sshPrivateKey, sshPublicKey } = await this.generateSshKeyPair();
    const tailscaleAuthKey = await this.createTailscaleAuthKey();
    const cloudConfig = this.buildCloudInitScript(sshPublicKey, tailscaleAuthKey);

    logger.info("creating Digital Ocean droplet", { jobId });

    // Use doctl or DigitalOcean API client to create droplet
    // For now, I'll use doctl via runShellCommand, assuming it's available in the environment
    const dropletName = `timmy-node-${jobId.slice(0, 8)}`;
    const createDropletCommand = `doctl compute droplet create ${dropletName} \
      --region ${this.config.doRegion} \
      --size ${this.config.doSize} \
      --image ubuntu-22-04-x64 \
      --enable-private-networking \
      --vpc-uuid <YOUR_VPC_UUID> \
      --user-data '${cloudConfig}' \
      --ssh-keys <YOUR_SSH_KEY_FINGERPRINT> \
      --format ID --no-header`; // Simplistic command, needs refinement for real use

    const createDropletOutput = await default_api.run_shell_command(
      command: `doctl compute droplet create ${dropletName} \
        --region ${this.config.doRegion} \
        --size ${this.config.doSize} \
        --image ubuntu-22-04-x64 \
        --enable-private-networking \
        --vpc-uuid ${this.config.doVpcUuid} \
        --user-data '${cloudConfig}' \
        --ssh-keys ${this.config.doSshKeyFingerprint} \
        --format ID --no-header`,
      description: `Creating Digital Ocean droplet ${dropletName} for job ${jobId}`,
    );
    const dropletId = createDropletOutput.output.trim();

    // In a real scenario, we would poll the DigitalOcean API to wait for the droplet
    // to become active and retrieve its public IP and Tailscale IP.
    // For now, we'll simulate this and retrieve dummy IPs.
    logger.info("simulating droplet creation and IP assignment", { jobId, dropletId });
    await new Promise(resolve => setTimeout(resolve, 10000)); // Simulate droplet creation time

    const nodeIp = `1.2.3.${Math.floor(Math.random() * 255)}`; // Dummy IP
    const tailscaleHostname = `${dropletName}.tail${this.config.tailscaleTailnet.slice(0, 4)}.ts.net`; // Dummy hostname
    const lnbitsUrl = `http://${nodeIp}:3000/lnbits`; // Dummy LNbits URL

    return {
      dropletId: dropletId,
      nodeIp: nodeIp,
      tailscaleHostname: tailscaleHostname,
      lnbitsUrl: lnbitsUrl,
      sshPrivateKey: sshPrivateKey,
    };
  }

  // Helper to generate SSH keypair using ssh-keygen
  private async generateSshKeyPair(): Promise<{ sshPrivateKey: string; sshPublicKey: string }> {
    logger.info("generating SSH keypair");
    const keyPath = `/tmp/id_rsa_${randomBytes(4).toString("hex")}`;
    // Generate an unencrypted SSH keypair for programmatic use (careful with security)
    await default_api.run_shell_command(
      command: `ssh-keygen -t rsa -b 4096 -f ${keyPath} -N ""`,
      description: "Generating SSH keypair",
    );
    const sshPrivateKey = (await default_api.run_shell_command(command: `cat ${keyPath}`)).output.trim();
    const sshPublicKey = (await default_api.run_shell_command(command: `cat ${keyPath}.pub`)).output.trim();
    await default_api.run_shell_command(command: `rm ${keyPath} ${keyPath}.pub`, description: "Cleaning up temporary SSH keys");
    return { sshPrivateKey, sshPublicKey };
  }

  // Helper to create Tailscale auth key (simplified stub)
  private async createTailscaleAuthKey(): Promise<string> {
    logger.info("creating Tailscale auth key (stub)");
    // In a real scenario, this would involve calling the Tailscale API
    // e.g., curl -X POST -H "Authorization: Bearer ${TAILSCALE_API_KEY}"
    // "https://api.tailscale.com/api/v2/tailnet/${TAILSCALE_TAILNET}/keys"
    await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate API call
    return `tskey-test-${randomBytes(16).toString("hex")}`;
  }

  // Helper to build cloud-init script
  private buildCloudInitScript(sshPublicKey: string, tailscaleAuthKey: string): string {
    logger.info("building cloud-init script");
    const setupScriptUrl = `http://143.198.27.163:3000/replit/timmy-tower/raw/branch/main/infrastructure/setup.sh`;
    const bitcoinConfUrl = `http://143.198.27.163:3000/replit/timmy-tower/raw/branch/main/infrastructure/configs/bitcoin.conf`;
    const lndConfUrl = `http://143.198.27.163:3000/replit/timmy-tower/raw/branch/main/infrastructure/configs/lnd.conf`;
    const dockerComposeUrl = `http://143.198.27.163:3000/replit/timmy-tower/raw/branch/main/infrastructure/docker-compose.yml`;
    const lndInitUrl = `http://143.198.27.163:3000/replit/timmy-tower/raw/branch/main/infrastructure/lnd-init.sh`;
    const sweepUrl = `http://143.198.27.163:3000/replit/timmy-tower/raw/branch/main/infrastructure/sweep.sh`;
    const sweepConfExampleUrl = `http://143.198.27.163:3000/replit/timmy-tower/raw/branch/main/infrastructure/sweep.conf.example`;
    const opsUrl = `http://143.198.27.163:3000/replit/timmy-tower/raw/branch/main/infrastructure/ops.sh`;

    return `
#cloud-config
users:
  - name: ubuntu
    ssh_authorized_keys:
      - ${sshPublicKey}
    sudo: ALL=(ALL) NOPASSWD:ALL

write_files:
  - path: /root/setup.sh
    permissions: '0755'
    content: |
      #!/usr/bin/env bash
      curl -s ${setupScriptUrl} > /root/setup.sh
  - path: /root/configs/bitcoin.conf
    content: |
      curl -s ${bitcoinConfUrl} > /root/configs/bitcoin.conf
  - path: /root/configs/lnd.conf
    content: |
      curl -s ${lndConfUrl} > /root/configs/lnd.conf
  - path: /root/docker-compose.yml
    content: |
      curl -s ${dockerComposeUrl} > /root/docker-compose.yml
  - path: /root/lnd-init.sh
    permissions: '0755'
    content: |
      curl -s ${lndInitUrl} > /root/lnd-init.sh
  - path: /root/sweep.sh
    permissions: '0755'
    content: |
      curl -s ${sweepUrl} > /root/sweep.sh
  - path: /root/sweep.conf.example
    content: |
      curl -s ${sweepConfExampleUrl} > /root/sweep.conf.example
  - path: /root/ops.sh
    permissions: '0755'
    content: |
      curl -s ${opsUrl} > /root/ops.sh

runcmd:
  - mkdir -p /root/configs
  - curl -s ${setupScriptUrl} > /tmp/setup.sh
  - chmod +x /tmp/setup.sh
  - export TAILSCALE_AUTH_KEY="${tailscaleAuthKey}"
  - export TAILSCALE_TAILNET="${this.config.tailscaleTailnet}"
  - /tmp/setup.sh
`;

export const provisionerService = new ProvisionerService();