import type { Customer } from '../billing/middleware.js';
import type { OAuthCredentials } from '../multitenancy/credential-store.js';
import { createToolAudit } from '../multitenancy/audit-log.js';

const TIKTOK_API_BASE = 'https://open.tiktokapis.com/v2';

function getEnvToken(account: string): string {
  const envKey = `TIKTOK_${account.toUpperCase()}_ACCESS_TOKEN`;
  return process.env[envKey] ?? process.env.TIKTOK_DEFAULT_ACCESS_TOKEN ?? '';
}

async function resolveToken(args: { account?: string }, customer?: Customer): Promise<string> {
  if (customer) {
    const creds = await customer.getCredential<OAuthCredentials>('tiktok');
    if (!creds) throw new Error('TikTok not connected for this account');
    return creds.accessToken;
  }
  const token = getEnvToken(args.account ?? 'default');
  if (!token) throw new Error('Missing TikTok credentials. Set TIKTOK_{ACCOUNT}_ACCESS_TOKEN');
  return token;
}

async function tiktokRequest(
  endpoint: string,
  accessToken: string,
  method: 'GET' | 'POST' = 'GET',
  body?: unknown,
  params?: Record<string, string>
) {
  const url = new URL(`${TIKTOK_API_BASE}${endpoint}`);
  if (params) {
    Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
  }

  const res = await fetch(url.toString(), {
    method,
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: body ? JSON.stringify(body) : undefined,
    signal: AbortSignal.timeout(15000),
  });

  const data = await res.json();
  if (data.error?.code && data.error.code !== 'ok') {
    throw new Error(`TikTok API error: ${data.error.message ?? data.error.code}`);
  }
  return data.data ?? data;
}

export async function getUserProfile(
  args: { account?: string },
  customer?: Customer
): Promise<{
  open_id: string;
  display_name: string;
  avatar_url?: string;
  bio?: string;
  is_verified: boolean;
  follower_count: number;
  following_count: number;
  likes_count: number;
  video_count: number;
}> {
  const accessToken = await resolveToken(args, customer);

  const fields = 'open_id,union_id,avatar_url,display_name,bio_description,is_verified,follower_count,following_count,likes_count,video_count';
  const data = await tiktokRequest('/user/info/', accessToken, 'GET', undefined, { fields });
  const u = data.user ?? data;

  return {
    open_id: u.open_id ?? '',
    display_name: u.display_name ?? '',
    avatar_url: u.avatar_url,
    bio: u.bio_description,
    is_verified: u.is_verified ?? false,
    follower_count: u.follower_count ?? 0,
    following_count: u.following_count ?? 0,
    likes_count: u.likes_count ?? 0,
    video_count: u.video_count ?? 0,
  };
}

export async function getCreatorInfo(
  args: { account?: string },
  customer?: Customer
): Promise<{
  creator_username: string;
  creator_nickname: string;
  creator_avatar_url?: string;
  privacy_level_options: string[];
  max_video_post_duration_sec: number;
  comment_disabled: boolean;
  duet_disabled: boolean;
  stitch_disabled: boolean;
}> {
  const accessToken = await resolveToken(args, customer);

  const data = await tiktokRequest('/post/publish/creator_info/query/', accessToken, 'POST', {});
  const c = data;

  return {
    creator_username: c.creator_username ?? '',
    creator_nickname: c.creator_nickname ?? '',
    creator_avatar_url: c.creator_avatar_url,
    privacy_level_options: c.privacy_level_options ?? [],
    max_video_post_duration_sec: c.max_video_post_duration_sec ?? 0,
    comment_disabled: c.comment_disabled ?? false,
    duet_disabled: c.duet_disabled ?? false,
    stitch_disabled: c.stitch_disabled ?? false,
  };
}

export async function createVideo(
  args: { video_url: string; title?: string; description?: string; account?: string },
  customer?: Customer
): Promise<{ publish_id: string; status: string }> {
  const audit = customer ? createToolAudit(customer.id, 'tiktok:createVideo') : null;
  const auditArgs = { title: args.title };
  const accessToken = await resolveToken(args, customer);

  // Step 1: query creator info to get valid privacy levels (sandbox may not support PUBLIC_TO_EVERYONE)
  const creatorInfo = await getCreatorInfo(args, customer);
  const options = creatorInfo.privacy_level_options;
  // Unaudited apps MUST post SELF_ONLY; prefer that when public isn't available
  const privacyLevel = options.includes('PUBLIC_TO_EVERYONE')
    ? 'PUBLIC_TO_EVERYONE'
    : options.includes('SELF_ONLY')
      ? 'SELF_ONLY'
      : options[0] ?? 'SELF_ONLY';

  // Step 2: initialise upload
  const init = await tiktokRequest('/post/publish/video/init/', accessToken, 'POST', {
    post_info: {
      title: args.title ?? '',
      description: args.description ?? '',
      disable_duet: false,
      disable_comment: false,
      disable_stitch: false,
      privacy_level: privacyLevel,
    },
    source_info: {
      source: 'PULL_FROM_URL',
      video_url: args.video_url,
    },
  });

  const publishId = init.publish_id ?? '';

  try {
    const result = { publish_id: publishId, status: 'processing' };
    if (audit) await audit.success(auditArgs);
    return result;
  } catch (err) {
    if (audit) await audit.error(auditArgs, String(err));
    throw err;
  }
}

export async function getVideoStatus(
  args: { publish_id: string; account?: string },
  customer?: Customer
): Promise<{ publish_id: string; status: string; fail_reason?: string }> {
  const accessToken = await resolveToken(args, customer);

  const data = await tiktokRequest('/post/publish/status/fetch/', accessToken, 'POST', {
    publish_id: args.publish_id,
  });

  return {
    publish_id: args.publish_id,
    status: String(data.status ?? 'unknown'),
    fail_reason: data.fail_reason as string | undefined,
  };
}
