import type { AsukaLicenseEntitlements, AsukaLicenseHttpOptions, AsukaLicenseTokenInput } from './license.types';
import { DEFAULT_ASUKA_API_BASE_URL } from '../apiEndpoints';

export const DEFAULT_ASUKA_LICENSE_SERVER_BASE_URL = DEFAULT_ASUKA_API_BASE_URL;

export const ASUKA_LICENSE_SERVER_ENDPOINTS = {
  publicKey: '/v1/licenses/public-key',
  resolve: '/v1/licenses/resolve',
  refresh: '/v1/licenses/refresh',
  deactivate: '/v1/licenses/deactivate',
  optimizeGlb: '/v1/assets/optimize-glb',
} as const;

export type AsukaLicenseServerErrorCode =
  | 'invalid_request'
  | 'invalid_license_key'
  | 'license_inactive'
  | 'license_expired'
  | 'activation_limit_reached'
  | 'domain_not_allowed'
  | 'instance_not_allowed'
  | 'product_mismatch'
  | 'signature_generation_failed'
  | 'rate_limited'
  | 'server_error';

export interface AsukaLicenseServerMeta {
  requestId?: string;
  serverTime?: string;
  apiVersion?: string;
}

export interface AsukaLicenseResolveRequest {
  key: string;
  product?: string;
  audience?: string;
  channel?: string;
  domain: string;
  instanceId: string;
  version?: string | null;
  acceptCompactToken?: boolean;
}

export interface AsukaLicenseRefreshRequest extends AsukaLicenseResolveRequest {}

export interface AsukaLicenseDeactivateRequest {
  key?: string;
  product?: string;
  channel?: string;
  domain?: string | null;
  instanceId?: string | null;
  version?: string | null;
}

export interface AsukaLicenseResolveResponseData {
  token: string;
  tokenType?: 'compact';
  entitlementsPreview?: Partial<AsukaLicenseEntitlements>;
}

export interface AsukaLicenseDeactivateResponseData {
  deactivated: boolean;
}

export interface AsukaLicensePublicKeyResponseData {
  publicKey: string;
  alg?: 'Ed25519' | 'EdDSA' | string;
  kid?: string;
  issuer?: string;
  audience?: string | string[];
  product?: string;
}

export interface AsukaLicenseServerError {
  code: AsukaLicenseServerErrorCode | string;
  message: string;
  retryable?: boolean;
  details?: Record<string, unknown>;
}

export interface AsukaLicenseServerSuccessEnvelope<T> {
  ok: true;
  data: T;
  meta?: AsukaLicenseServerMeta;
}

export interface AsukaLicenseServerErrorEnvelope {
  ok: false;
  error: AsukaLicenseServerError;
  meta?: AsukaLicenseServerMeta;
}

export type AsukaLicenseResolveResponseEnvelope =
  | AsukaLicenseServerSuccessEnvelope<AsukaLicenseResolveResponseData>
  | AsukaLicenseServerErrorEnvelope;

export type AsukaLicenseRefreshResponseEnvelope = AsukaLicenseResolveResponseEnvelope;

export type AsukaLicenseDeactivateResponseEnvelope =
  | AsukaLicenseServerSuccessEnvelope<AsukaLicenseDeactivateResponseData>
  | AsukaLicenseServerErrorEnvelope;

export type AsukaLicensePublicKeyResponseEnvelope =
  | AsukaLicenseServerSuccessEnvelope<AsukaLicensePublicKeyResponseData>
  | AsukaLicenseServerErrorEnvelope;

export class AsukaLicenseServerErrorResponse extends Error {
  code: string;
  retryable: boolean;
  details?: Record<string, unknown>;
  meta?: AsukaLicenseServerMeta;

  constructor(error: AsukaLicenseServerError, meta?: AsukaLicenseServerMeta) {
    super(error.message || 'License server error');
    this.name = 'AsukaLicenseServerErrorResponse';
    this.code = String(error.code || 'server_error');
    this.retryable = error.retryable === true;
    this.details = error.details;
    this.meta = meta;
  }
}

export function isStandardLicenseServerEnvelope(
  value: unknown,
): value is AsukaLicenseResolveResponseEnvelope | AsukaLicensePublicKeyResponseEnvelope {
  return Boolean(value && typeof value === 'object' && 'ok' in (value as any));
}

export function unwrapStandardLicenseToken(value: unknown): AsukaLicenseTokenInput | null {
  if (!isStandardLicenseServerEnvelope(value)) return null;
  if (value.ok !== true) {
    throw new AsukaLicenseServerErrorResponse((value as AsukaLicenseServerErrorEnvelope).error, value.meta);
  }
  const token = (value as AsukaLicenseServerSuccessEnvelope<AsukaLicenseResolveResponseData>).data?.token;
  return typeof token === 'string' && token.trim() ? token : null;
}

function isRawLicensePublicKeyPayload(value: unknown): value is AsukaLicensePublicKeyResponseData {
  return Boolean(
    value &&
      typeof value === 'object' &&
      typeof (value as any).publicKey === 'string' &&
      String((value as any).publicKey).trim(),
  );
}

export function unwrapStandardLicensePublicKey(value: unknown): AsukaLicensePublicKeyResponseData | null {
  if (isRawLicensePublicKeyPayload(value)) {
    return value;
  }

  if (!isStandardLicenseServerEnvelope(value)) return null;
  if (value.ok !== true) {
    throw new AsukaLicenseServerErrorResponse((value as AsukaLicenseServerErrorEnvelope).error, value.meta);
  }
  const data = (value as AsukaLicenseServerSuccessEnvelope<AsukaLicensePublicKeyResponseData>).data;
  if (!data || typeof data !== 'object') return null;
  if (typeof data.publicKey !== 'string' || !data.publicKey.trim()) return null;
  return data;
}

export async function fetchStandardLicensePublicKey(
  init: RequestInit = {},
): Promise<AsukaLicensePublicKeyResponseData> {
  const response = await fetch(
    `${DEFAULT_ASUKA_LICENSE_SERVER_BASE_URL}${ASUKA_LICENSE_SERVER_ENDPOINTS.publicKey}`,
    {
      method: 'GET',
      ...init,
    },
  );

  const contentType = String(response.headers.get('content-type') ?? '').toLowerCase();
  const raw = contentType.includes('application/json') ? await response.json() : await response.text();

  if (!response.ok) {
    if (isStandardLicenseServerEnvelope(raw) && raw.ok === false) {
      throw new AsukaLicenseServerErrorResponse(raw.error, raw.meta);
    }
    throw new Error(`License public-key request failed (${response.status})`);
  }

  const data = unwrapStandardLicensePublicKey(raw);
  if (!data) {
    throw new Error('Invalid public-key response');
  }

  return data;
}

export function createDefaultLicenseHttpOptions(
  options: Omit<AsukaLicenseHttpOptions, 'endpoint' | 'refreshEndpoint' | 'deactivateEndpoint' | 'publicKeyEndpoint' | 'parseResponse'> = {},
): AsukaLicenseHttpOptions {
  return {
    ...options,
    endpoint: `${DEFAULT_ASUKA_LICENSE_SERVER_BASE_URL}${ASUKA_LICENSE_SERVER_ENDPOINTS.resolve}`,
    refreshEndpoint: `${DEFAULT_ASUKA_LICENSE_SERVER_BASE_URL}${ASUKA_LICENSE_SERVER_ENDPOINTS.refresh}`,
    deactivateEndpoint: `${DEFAULT_ASUKA_LICENSE_SERVER_BASE_URL}${ASUKA_LICENSE_SERVER_ENDPOINTS.deactivate}`,
    publicKeyEndpoint: `${DEFAULT_ASUKA_LICENSE_SERVER_BASE_URL}${ASUKA_LICENSE_SERVER_ENDPOINTS.publicKey}`,
    acceptCompactToken: options.acceptCompactToken ?? true,
    parseResponse: (payload: unknown) => {
      const token = unwrapStandardLicenseToken(payload);
      if (token) return token;
      return payload as any;
    },
  };
}
