import Taro from '@tarojs/taro';
import { appEnv, ensureRuntimeConfigLoaded } from '@/env';
import type { ApiErrorPayload, ApiSession, TenantContext } from '@/types';
import { buildApiHeaders, resolveApiAuthorization } from './api-auth';
import type { ApiAuthMode } from './api-auth';
import { getStorage, removeStorage, setStorage } from './storage';

const TENANT_KEY = 'tiku:tenant';
const SESSION_KEY = 'tiku:session';

export class ApiError extends Error {
  status: number;
  code: string;
  details?: unknown;

  constructor(payload: ApiErrorPayload) {
    super(payload.message);
    this.name = 'ApiError';
    this.status = payload.status;
    this.code = payload.code;
    this.details = payload.details;
  }
}

export function getTenantContext() {
  return getStorage<TenantContext>(TENANT_KEY);
}

export function saveTenantContext(tenant: TenantContext) {
  setStorage(TENANT_KEY, tenant);
}

export function clearTenantContext() {
  removeStorage(TENANT_KEY);
}

export function getSession() {
  return getStorage<ApiSession>(SESSION_KEY);
}

export function saveSession(session: ApiSession) {
  setStorage(SESSION_KEY, session);
}

export function clearSession() {
  removeStorage(SESSION_KEY);
}

export type { ApiAuthMode, SupabaseAccessTokenProvider } from './api-auth';
export { setSupabaseAccessTokenProviderForTest } from './api-auth';

function normalizeBaseUrl(baseUrl: string) {
  return baseUrl.replace(/\/+$/, '');
}

function buildUrl(path: string, query?: Record<string, string | number | boolean | null | undefined>) {
  const url = `${normalizeBaseUrl(appEnv.apiBaseUrl)}${path.startsWith('/') ? path : `/${path}`}`;
  const params = Object.entries(query || {})
    .filter(([, value]) => value !== undefined && value !== null && value !== '')
    .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
  return params.length ? `${url}?${params.join('&')}` : url;
}

export async function apiRequest<T>(
  path: string,
  options: {
    method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
    query?: Record<string, string | number | boolean | null | undefined>;
    body?: unknown;
    tenantId?: string | null;
    token?: string | null;
    authMode?: ApiAuthMode;
    headers?: Record<string, string>;
  } = {},
): Promise<T> {
  await ensureRuntimeConfigLoaded();
  const tenant = getTenantContext();
  const session = getSession();
  const hasTenantOverride = Object.prototype.hasOwnProperty.call(options, 'tenantId');
  const hasTokenOverride = Object.prototype.hasOwnProperty.call(options, 'token');
  const tenantId = hasTenantOverride ? options.tenantId : tenant?.tenantId ?? null;
  const authMode = options.authMode || 'auto';
  const token = await resolveApiAuthorization({
    authMode,
    hasTokenOverride,
    explicitToken: options.token,
    legacyToken: session?.token,
  });
  const headers = buildApiHeaders({ tenantId, token, extraHeaders: options.headers });

  const response = await Taro.request({
    url: buildUrl(path, options.query),
    method: options.method || 'GET',
    data: options.body === undefined ? undefined : options.body,
    header: headers,
  });
  const payload = (response.data || {}) as Record<string, unknown>;
  if (response.statusCode < 200 || response.statusCode >= 300) {
    if (response.statusCode === 401) clearSession();
    throw new ApiError({
      status: response.statusCode,
      code: String(payload.code || 'API_ERROR'),
      message: String(payload.message || '请求失败'),
      details: payload,
    });
  }
  return payload as T;
}

export async function resolveTenant(input: { host?: string; tenantCode?: string } = {}) {
  const payload = await apiRequest<{
    item?: TenantContext;
    tenant?: {
      id?: string;
      tenantId?: string;
      slug?: string;
    };
    branding?: TenantContext['branding'];
    features?: TenantContext['features'];
    adminFeatures?: TenantContext['adminFeatures'];
    publicConfig?: TenantContext['publicConfig'];
  }>('/api/tenant/resolve', {
    query: {
      host: input.host,
      tenantCode: input.tenantCode || appEnv.tenantCode,
    },
    tenantId: null,
    authMode: 'none',
  });
  const tenantId = payload.item?.tenantId || payload.tenant?.tenantId || payload.tenant?.id;
  if (!tenantId) throw new ApiError({ status: 500, code: 'TENANT_RESOLVE_INVALID', message: '租户解析结果缺少 tenantId' });
  const context: TenantContext = {
    tenantId,
    tenantSlug: payload.item?.tenantSlug || payload.tenant?.slug,
    host: input.host,
    branding: payload.item?.branding || payload.branding || {},
    features: payload.item?.features || payload.features || {},
    adminFeatures: payload.item?.adminFeatures || payload.adminFeatures || {},
    publicConfig: payload.item?.publicConfig || payload.publicConfig || {},
  };
  saveTenantContext(context);
  return context;
}
