import type { AsukaPermissions } from './permissions';
import type { AsukaLicenseFeature } from './license/license.types';
import { emitAsukaEvent, emitAsukaEventCancelable, emitAfter, emitBefore } from './events';

export type AsukaAgencyFeatureKey =
  | 'advancedPermissions'
  | 'whiteLabel'
  | 'apiAdvanced'
  | 'backgroundRemoval';

export interface AsukaBrandingConfig {
  productName?: string;
  logoUrl?: string;
  hidePoweredBy?: boolean;
  whiteLabel?: boolean;
  rootClassName?: string;
  cssVariables?: Record<string, string>;
}

export interface AsukaBackgroundRemovalInput {
  url?: string;
  file?: File | Blob;
  objectId?: string;
  metadata?: Record<string, unknown>;
}

export interface AsukaBackgroundRemovalResult {
  url?: string;
  blob?: Blob;
  dataUrl?: string;
  metadata?: Record<string, unknown>;
}

export interface AsukaBackgroundRemovalConfig {
  endpoint?: string;
  headers?: Record<string, string>;
  credentials?: RequestCredentials;
  handler?: (input: AsukaBackgroundRemovalInput) => Promise<AsukaBackgroundRemovalResult>;
}

export interface AsukaAgencyOptions {
  branding?: AsukaBrandingConfig;
  backgroundRemoval?: AsukaBackgroundRemovalConfig;
}

export interface AsukaAgencyBundle {
  version: 1;
  exportedAt: string;
  branding: AsukaBrandingConfig;
  permissions?: AsukaPermissions;
  layout?: unknown;
  theme?: unknown;
}

export interface AsukaAgencyRuntimeDeps {
  rootElement: HTMLElement;
  options?: AsukaAgencyOptions | null;
  canUseFeature: (feature: AsukaLicenseFeature | string) => boolean;
  getPermissions?: () => AsukaPermissions;
  setPermissions?: (patch: Partial<AsukaPermissions>) => void;
  exportLayout?: () => unknown;
  getThemeState?: () => unknown;
}

function nowIso(): string { return new Date().toISOString(); }

export function createAsukaAgencyRuntime(deps: AsukaAgencyRuntimeDeps) {
  let branding: AsukaBrandingConfig = { ...(deps.options?.branding ?? {}) };
  const backgroundRemoval = deps.options?.backgroundRemoval ?? {};
  const has = (feature: AsukaAgencyFeatureKey) => deps.canUseFeature(feature);
  const assertFeature = (feature: AsukaAgencyFeatureKey, action: string) => {
    if (has(feature)) return true;
    const reason = `${action} requires the ${feature} Pro or Agency entitlement.`;
    const ok = emitAsukaEventCancelable('before:agency.featureDenied', { feature, action, reason });
    if (!ok) return false;
    emitAsukaEvent('after:agency.featureDenied', { feature, action, reason });
    throw new Error(`[AsukaDesigner] ${reason}`);
  };
  const applyBrandingToRoot = () => {
    const root = deps.rootElement;
    if (!root) return;

    // Powered-by branding is visible on Free/Starter and hidden automatically on
    // Pro/Agency through the whiteLabel entitlement. Integrators can still force it
    // visible with branding.hidePoweredBy = false if they want to keep attribution.
    const whiteLabelEntitled = has('whiteLabel');
    const forceShowPoweredBy = branding.hidePoweredBy === false || branding.whiteLabel === false;
    const whiteLabelActive = whiteLabelEntitled && !forceShowPoweredBy;

    root.dataset.asukaWhiteLabel = whiteLabelActive ? 'true' : 'false';
    root.dataset.asukaPoweredByVisible = whiteLabelActive ? 'false' : 'true';
    if (typeof branding.productName === 'string') root.dataset.asukaBrandName = branding.productName;
    else delete root.dataset.asukaBrandName;
    if (typeof branding.logoUrl === 'string') root.dataset.asukaBrandLogo = branding.logoUrl;
    else delete root.dataset.asukaBrandLogo;
    if (branding.rootClassName) root.classList.add(...branding.rootClassName.split(/\s+/).filter(Boolean));
    Object.entries(branding.cssVariables ?? {}).forEach(([key, value]) => {
      if (key.startsWith('--')) root.style.setProperty(key, String(value));
    });
  };

  const api = {
    isAvailable: (feature: AsukaAgencyFeatureKey) => has(feature),
    refreshEntitlements: () => { applyBrandingToRoot(); return api.getAvailability(); },
    getAvailability: () => ({
      advancedPermissions: has('advancedPermissions'),
      whiteLabel: has('whiteLabel'),
      apiAdvanced: has('apiAdvanced'),
      backgroundRemoval: has('backgroundRemoval'),
    }),
    branding: {
      get: () => ({ ...branding, cssVariables: { ...(branding.cssVariables ?? {}) } }),
      apply: (patch: AsukaBrandingConfig) => {
        // Hiding powered-by branding is automatic on Pro/Agency. Explicit requests to
        // customize product identity still require the whiteLabel entitlement.
        if (
          patch?.whiteLabel === true ||
          patch?.hidePoweredBy === true ||
          typeof patch?.productName === 'string' ||
          typeof patch?.logoUrl === 'string'
        ) {
          assertFeature('whiteLabel', 'Applying white-label branding');
        }
        branding = { ...branding, ...(patch ?? {}) };
        applyBrandingToRoot();
        emitAfter('agency.branding.changed', { branding: { ...branding } });
        return { ...branding };
      },
      reset: () => {
        branding = {};
        try {
          delete deps.rootElement.dataset.asukaBrandName;
          delete deps.rootElement.dataset.asukaBrandLogo;
          deps.rootElement.dataset.asukaWhiteLabel = 'false';
          deps.rootElement.dataset.asukaPoweredByVisible = 'true';
        } catch {}
        emitAfter('agency.branding.changed', { branding: {} });
      },
    },
    permissions: {
      applyPolicy: (patch: Partial<AsukaPermissions>) => {
        assertFeature('advancedPermissions', 'Applying advanced permissions');
        deps.setPermissions?.(patch);
        emitAfter('agency.permissions.policyApplied', { patch });
      },
      getCurrent: () => deps.getPermissions?.() ?? null,
    },
    backgroundRemoval: {
      remove: async (input: AsukaBackgroundRemovalInput) => {
        assertFeature('backgroundRemoval', 'Removing image backgrounds');
        if (!input?.url && !input?.file) throw new Error('[AsukaDesigner] backgroundRemoval.remove requires a url or file.');
        emitBefore('agency.backgroundRemoval.remove', { input: { ...input, file: input.file ? '[binary]' : undefined } });
        const result = backgroundRemoval.handler
          ? await backgroundRemoval.handler(input)
          : backgroundRemoval.endpoint
            ? await (async () => {
                const hasFile = !!input.file;
                const response = await fetch(backgroundRemoval.endpoint!, {
                  method: 'POST',
                  headers: hasFile ? backgroundRemoval.headers : { 'Content-Type': 'application/json', ...(backgroundRemoval.headers ?? {}) },
                  credentials: backgroundRemoval.credentials,
                  body: hasFile
                    ? (() => {
                        const body = new FormData();
                        body.append('file', input.file as Blob);
                        if (input.objectId) body.append('objectId', input.objectId);
                        if (input.metadata) body.append('metadata', JSON.stringify(input.metadata));
                        return body;
                      })()
                    : JSON.stringify({ url: input.url, objectId: input.objectId, metadata: input.metadata }),
                });
                if (!response.ok) throw new Error(`[AsukaDesigner] Background removal request failed: ${response.status}`);
                const contentType = response.headers.get('content-type') || '';
                if (contentType.includes('application/json')) return await response.json() as AsukaBackgroundRemovalResult;
                return { blob: await response.blob() } as AsukaBackgroundRemovalResult;
              })()
            : null;
        if (!result) throw new Error('[AsukaDesigner] No background removal handler or endpoint configured.');
        emitAfter('agency.backgroundRemoval.remove', { result: { ...result, blob: result.blob ? '[binary]' : undefined } });
        return result;
      },
    },
    exportBundle: (): AsukaAgencyBundle => {
      assertFeature('apiAdvanced', 'Exporting Agency configuration');
      return { version: 1, exportedAt: nowIso(), branding: api.branding.get(), permissions: deps.getPermissions?.(), layout: deps.exportLayout?.(), theme: deps.getThemeState?.() };
    },
    importBundle: (bundle: Partial<AsukaAgencyBundle>) => {
      assertFeature('apiAdvanced', 'Importing Agency configuration');
      if (bundle.branding) api.branding.apply(bundle.branding);
      if (bundle.permissions) deps.setPermissions?.(bundle.permissions);
      emitAfter('agency.bundle.imported', { bundle });
      return api.exportBundle();
    },
  };
  applyBrandingToRoot();
  return api;
}
export type AsukaAgencyApi = ReturnType<typeof createAsukaAgencyRuntime>;
