/**
 * SFTP adapter — uploads the standalone bundle to any SSH/SFTP host (Hostinger, cPanel,
 * VPS, …). Auth via password or SSH private key (stored as a JSON credentials secret).
 * Requires the optional `ssh2-sftp-client` dependency; if it isn't installed the deploy
 * fails with a clear, non-crashing message (the rest of the system is unaffected).
 */

import path from 'path';
import { HostingAdapter, DeployInput, DeployResult, parseCreds, optionalRequire, normalizeHost, resolveRemoteRoot } from './types';

export const sftpAdapter: HostingAdapter = {
  provider: 'sftp',

  async validate(connection, secret) {
    const SftpClient = optionalRequire('ssh2-sftp-client');
    if (!SftpClient) return { ok: false, error: 'Server missing "ssh2-sftp-client" dependency. Run: npm i ssh2-sftp-client' };
    // Accept a host pasted as a URL — otherwise DNS is handed the whole string and
    // fails with `getaddrinfo ENOTFOUND https://host/`.
    const { host, port: hostPort } = normalizeHost(connection.host);
    if (!host || !connection.username) return { ok: false, error: 'Host and username are required' };
    const creds = parseCreds(secret);
    const sftp = new SftpClient();
    try {
      await sftp.connect({
        host, port: connection.port || hostPort || 22, username: connection.username,
        password: creds.password, privateKey: creds.privateKey, passphrase: creds.passphrase, readyTimeout: 20000,
      });
      return { ok: true };
    } catch (err: any) {
      return { ok: false, error: err?.message || 'SFTP connection failed' };
    } finally {
      try { await sftp.end(); } catch { /* ignore */ }
    }
  },

  async deploy({ bundle, connection, secret }: DeployInput): Promise<DeployResult> {
    const SftpClient = optionalRequire('ssh2-sftp-client');
    if (!SftpClient) throw new Error('Server missing "ssh2-sftp-client" dependency. Run: npm i ssh2-sftp-client');
    const { host, port: hostPort } = normalizeHost(connection.host);
    if (!host || !connection.username) throw new Error('SFTP connection needs host and username.');

    const creds = parseCreds(secret);
    const remoteRoot = resolveRemoteRoot(connection.remotePath);
    const sftp = new SftpClient();
    try {
      await sftp.connect({
        host, port: connection.port || hostPort || 22, username: connection.username,
        password: creds.password, privateKey: creds.privateKey, passphrase: creds.passphrase, readyTimeout: 30000,
      });

      let target = remoteRoot.path;
      if (target && !remoteRoot.explicit) {
        // Only a preference, not an instruction — see the FTP adapter. Creating
        // public_html inside an account that already lands in the web root would
        // publish the site to https://domain/public_html/ instead of /.
        const exists = await sftp.exists(target).catch(() => false);
        if (!exists) {
          console.log(`[SFTP] "${target}" not found at the login directory — publishing to the login directory (it is already the web root).`);
          target = '';
        }
      } else if (target) {
        // Explicitly named: create it if this is a fresh account.
        try { await sftp.mkdir(target, true); } catch { /* already exists */ }
      }

      // Ensure directories then upload each file.
      const madeDirs = new Set<string>();
      for (const f of bundle.files) {
        const remotePath = target ? path.posix.join(target, f.relPath) : f.relPath;
        const remoteDir = path.posix.dirname(remotePath);
        if (remoteDir !== '.' && !madeDirs.has(remoteDir)) {
          try { await sftp.mkdir(remoteDir, true); } catch { /* may already exist */ }
          madeDirs.add(remoteDir);
        }
        await sftp.put(f.absPath, remotePath);
      }
    } finally {
      try { await sftp.end(); } catch { /* ignore */ }
    }

    return { deployUrl: connection.baseUrl || '', providerDeployId: bundle.hash };
  },
};
