import { Injectable, Logger } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import * as crypto from "crypto";

export interface PaddleWebhookEvent {
  event_id: string;
  event_type: string;
  occurred_at: string;
  notification_id: string;
  data: Record<string, unknown>;
}
@Injectable()
export class PaddleService {
  private readonly logger = new Logger(PaddleService.name);
  private readonly apiKey: string;
  private readonly webhookSecret: string;
  private readonly environment: "sandbox" | "production";
  private readonly baseUrl: string;

  constructor(private readonly config: ConfigService) {
    this.apiKey = this.config.get<string>("PADDLE_API_KEY", "");
    this.webhookSecret = this.config.get<string>("PADDLE_WEBHOOK_SECRET", "");
    this.environment = this.config.get<"sandbox" | "production">(
      "PADDLE_ENVIRONMENT",
      "sandbox",
    );
    this.baseUrl =
      this.environment === "production"
        ? "https://api.paddle.com"
        : "https://sandbox-api.paddle.com";
  }

  /**
   * Verify Paddle webhook signature (Paddle Billing v2)
   */
  verifyWebhookSignature(rawBody: string, signatureHeader: string): boolean {
    if (!this.webhookSecret) {
      this.logger.warn(
        "PADDLE_WEBHOOK_SECRET not configured, skipping verification",
      );
      return false;
    }

    try {
      // Paddle signature format: ts=TIMESTAMP;h1=HASH
      const parts = signatureHeader.split(";");
      const tsValue = parts
        .find((p) => p.startsWith("ts="))
        ?.replace("ts=", "");
      const h1Value = parts
        .find((p) => p.startsWith("h1="))
        ?.replace("h1=", "");

      if (!tsValue || !h1Value) {
        this.logger.warn("Invalid Paddle signature format");
        return false;
      }

      // Compute expected signature: HMAC-SHA256(ts + ':' + rawBody)
      const signedPayload = `${tsValue}:${rawBody}`;
      const expectedSignature = crypto
        .createHmac("sha256", this.webhookSecret)
        .update(signedPayload)
        .digest("hex");

      return crypto.timingSafeEqual(
        Buffer.from(h1Value),
        Buffer.from(expectedSignature),
      );
    } catch (error: unknown) {
      const err = error as Error;
      this.logger.error(
        `Webhook signature verification failed: ${err.message}`,
      );
      return false;
    }
  }

  /**
   * Cancel a Paddle subscription
   */
  async cancelSubscription(
    paddleSubscriptionId: string,
    effectiveFrom:
      | "immediately"
      | "next_billing_period" = "next_billing_period",
  ): Promise<void> {
    const url = `${this.baseUrl}/subscriptions/${paddleSubscriptionId}/cancel`;

    const response = await fetch(url, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${this.apiKey}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ effective_from: effectiveFrom }),
    });

    if (!response.ok) {
      const body = await response.text();
      this.logger.error(`Paddle cancel failed: ${response.status} ${body}`);
      throw new Error(
        `Failed to cancel Paddle subscription: ${response.status}`,
      );
    }

    this.logger.log(
      `Paddle subscription ${paddleSubscriptionId} cancelled (effective: ${effectiveFrom})`,
    );
  }

  /**
   * Get subscription details from Paddle
   */
  async getSubscription(
    paddleSubscriptionId: string,
  ): Promise<Record<string, unknown>> {
    const url = `${this.baseUrl}/subscriptions/${paddleSubscriptionId}`;

    const response = await fetch(url, {
      method: "GET",
      headers: {
        Authorization: `Bearer ${this.apiKey}`,
        "Content-Type": "application/json",
      },
    });

    if (!response.ok) {
      throw new Error(`Failed to get Paddle subscription: ${response.status}`);
    }

    const data = (await response.json()) as { data: Record<string, unknown> };
    return data.data;
  }

  /**
   * Map Paddle price ID to our internal plan
   */
  mapPriceIdToPlan(priceId: string): {
    plan: "plus" | "premium";
    interval: "monthly" | "yearly";
  } | null {
    const mapping: Record<
      string,
      { plan: "plus" | "premium"; interval: "monthly" | "yearly" }
    > = {
      [this.config.get<string>("PADDLE_PLUS_MONTHLY_PRICE_ID", "")]: {
        plan: "plus",
        interval: "monthly",
      },
      [this.config.get<string>("PADDLE_PLUS_YEARLY_PRICE_ID", "")]: {
        plan: "plus",
        interval: "yearly",
      },
      [this.config.get<string>("PADDLE_PREMIUM_MONTHLY_PRICE_ID", "")]: {
        plan: "premium",
        interval: "monthly",
      },
      [this.config.get<string>("PADDLE_PREMIUM_YEARLY_PRICE_ID", "")]: {
        plan: "premium",
        interval: "yearly",
      },
    };

    // Remove empty key (from missing env vars)
    delete mapping[""];

    return mapping[priceId] ?? null;
  }

  /**
   * Get the Paddle price ID for a given plan and interval
   */
  getPriceId(plan: "plus" | "premium", interval: "monthly" | "yearly"): string {
    const key = `PADDLE_${plan.toUpperCase()}_${interval.toUpperCase()}_PRICE_ID`;
    const priceId = this.config.get<string>(key, "");

    if (!priceId) {
      throw new Error(
        `Price ID not configured for ${plan} ${interval} (env: ${key})`,
      );
    }

    return priceId;
  }

  /**
   * Get the client-side token for Paddle.js
   */
  getClientToken(): string {
    return this.config.get<string>("PADDLE_CLIENT_TOKEN", "");
  }

  /**
   * Get the Paddle environment
   */
  getEnvironment(): "sandbox" | "production" {
    return this.environment;
  }
}
