import { getErrorMessage } from "@bff/core/utils/error.util";
import { Logger } from "nestjs-pino";
import { Injectable, Inject } from "@nestjs/common";
import { WhmcsConnectionOrchestratorService } from "../connection/services/whmcs-connection-orchestrator.service";
import type { WhmcsCreateSsoTokenParams } from "@customer-portal/domain/customer";
import type { WhmcsSsoResponse } from "@customer-portal/domain/customer";

@Injectable()
export class WhmcsSsoService {
  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly connectionService: WhmcsConnectionOrchestratorService
  ) {}

  /**
   * Create SSO token for WHMCS access
   */
  async createSsoToken(
    clientId: number,
    destination?: string,
    ssoRedirectPath?: string
  ): Promise<{ url: string; expiresAt: string }> {
    try {
      const params: WhmcsCreateSsoTokenParams = {
        client_id: clientId,
        ...(destination && { destination }),
        ...(ssoRedirectPath && { sso_redirect_path: ssoRedirectPath }),
      };

      const response: WhmcsSsoResponse = await this.connectionService.createSsoToken(params);

      const url = this.resolveRedirectUrl(response.redirect_url);
      this.debugLogRedirectHost(url);
      const result = {
        url,
        expiresAt: new Date(Date.now() + 60000).toISOString(), // ~60 seconds (per WHMCS spec)
      };

      this.logger.log(`Created SSO token for client ${clientId}`, {
        destination,
        ssoRedirectPath,
      });
      return result;
    } catch (error) {
      this.logger.error(`Failed to create SSO token for client ${clientId}`, {
        error: getErrorMessage(error),
        destination,
        ssoRedirectPath,
      });
      throw error;
    }
  }

  /**
   * Helper function to create SSO links for invoices (following WHMCS best practices)
   */
  async whmcsSsoForInvoice(
    clientId: number,
    invoiceId: number,
    target: "view" | "download" | "pay"
  ): Promise<string> {
    let path: string;

    switch (target) {
      case "pay":
        // Direct payment page using Friendly URLs
        path = `index.php?rp=/invoice/${invoiceId}/pay`;
        break;
      case "download":
        // PDF download
        path = `dl.php?type=i&id=${invoiceId}`;
        break;
      case "view":
      default:
        // Invoice view page
        path = `viewinvoice.php?id=${invoiceId}`;
        break;
    }

    const params: WhmcsCreateSsoTokenParams = {
      client_id: clientId,
      destination: "sso:custom_redirect",
      sso_redirect_path: path,
    };

    const response: WhmcsSsoResponse = await this.connectionService.createSsoToken(params);

    // Return the 60s, one-time URL (resolved to absolute)
    const url = this.resolveRedirectUrl(response.redirect_url);
    this.debugLogRedirectHost(url);
    return url;
  }

  /**
   * Create SSO token for direct WHMCS admin access
   */
  async createAdminSsoToken(
    clientId: number,
    adminPath?: string
  ): Promise<{ url: string; expiresAt: string }> {
    try {
      const params: WhmcsCreateSsoTokenParams = {
        client_id: clientId,
        destination: adminPath || "clientarea.php",
      };

      const response: WhmcsSsoResponse = await this.connectionService.createSsoToken(params);

      const url = this.resolveRedirectUrl(response.redirect_url);
      this.debugLogRedirectHost(url);
      const result = {
        url,
        expiresAt: new Date(Date.now() + 60000).toISOString(), // ~60 seconds
      };

      this.logger.log(`Created admin SSO token for client ${clientId}`, {
        adminPath,
      });
      return result;
    } catch (error) {
      this.logger.error(`Failed to create admin SSO token for client ${clientId}`, {
        error: getErrorMessage(error),
        adminPath,
      });
      throw error;
    }
  }

  /**
   * Create SSO token for specific WHMCS module/page
   */
  async createModuleSsoToken(
    clientId: number,
    module: string,
    action?: string,
    params?: Record<string, unknown>
  ): Promise<{ url: string; expiresAt: string }> {
    try {
      // Build the module path
      let modulePath = `index.php?m=${module}`;
      if (action) {
        modulePath += `&a=${action}`;
      }
      if (params) {
        const stringParams: Record<string, string> = {};
        for (const [key, value] of Object.entries(params)) {
          if (value === undefined || value === null) continue;
          stringParams[key] = typeof value === "string" ? value : JSON.stringify(value);
        }
        const queryParams = new URLSearchParams(stringParams).toString();
        if (queryParams) {
          modulePath += `&${queryParams}`;
        }
      }

      const ssoParams: WhmcsCreateSsoTokenParams = {
        client_id: clientId,
        destination: "sso:custom_redirect",
        sso_redirect_path: modulePath,
      };

      const response: WhmcsSsoResponse = await this.connectionService.createSsoToken(ssoParams);

      const url = this.resolveRedirectUrl(response.redirect_url);
      this.debugLogRedirectHost(url);
      const result = {
        url,
        expiresAt: new Date(Date.now() + 60000).toISOString(), // ~60 seconds
      };

      this.logger.log(`Created module SSO token for client ${clientId}`, {
        module,
        action,
        modulePath,
      });
      return result;
    } catch (error) {
      this.logger.error(`Failed to create module SSO token for client ${clientId}`, {
        error: getErrorMessage(error),
        module,
        action,
        params,
      });
      throw error;
    }
  }

  /**
   * Ensure the returned redirect URL is absolute and points to the active WHMCS base URL.
   * WHMCS typically returns an absolute URL, but we normalize in case it's relative.
   */
  private resolveRedirectUrl(redirectUrl: string): string {
    if (!redirectUrl) return redirectUrl;
    const base = this.connectionService.getBaseUrl().replace(/\/+$/, "");
    const isAbsolute = /^https?:\/\//i.test(redirectUrl);
    if (!isAbsolute) {
      const path = redirectUrl.replace(/^\/+/, "");
      return `${base}/${path}`;
    }
    // Absolute URL returned by WHMCS — return as-is
    return redirectUrl;
  }

  /**
   * Debug helper: log only the host of the SSO URL (never the token) in non-production.
   */
  private debugLogRedirectHost(url: string): void {
    if (process.env.NODE_ENV === "production") return;
    try {
      const target = new URL(url);
      const base = new URL(this.connectionService.getBaseUrl());
      this.logger.debug("WHMCS SSO redirect host", {
        redirectHost: target.host,
        redirectOrigin: target.origin,
        baseOrigin: base.origin,
      });
    } catch {
      // Ignore parse errors silently
    }
  }
}
