/**
 * API Client - Gọi backend CuCu Note
 * Configurable server URL + tag/workspace fetching
 */

// Default API base URL — empty means user must configure in Settings
const DEFAULT_API_BASE_URL = 'http://160.191.50.138:3001';

// ========== Config ==========

async function getApiBase(): Promise<string> {
  const result = await chrome.storage.local.get(['apiBaseUrl']);
  const baseUrl = result.apiBaseUrl || DEFAULT_API_BASE_URL;
  return `${baseUrl}/api/v1`;
}

export async function getApiBaseUrl(): Promise<string> {
  const result = await chrome.storage.local.get(['apiBaseUrl']);
  return result.apiBaseUrl || DEFAULT_API_BASE_URL;
}

export async function setApiBaseUrl(url: string): Promise<void> {
  // Basic URL validation
  const cleaned = url.trim().replace(/\/+$/, '');
  if (cleaned && !cleaned.startsWith('http://') && !cleaned.startsWith('https://')) {
    throw new Error('URL must start with http:// or https://');
  }
  await chrome.storage.local.set({ apiBaseUrl: cleaned });
}

// ========== Auth ==========

async function getAuthToken(): Promise<string | null> {
  const result = await chrome.storage.local.get(['clerkSessionToken', 'authToken']);
  if (result.clerkSessionToken) return result.clerkSessionToken;
  return result.authToken || null;
}

export async function setAuthToken(token: string): Promise<void> {
  await chrome.storage.local.set({ authToken: token });
}

export async function getAuthStatus(): Promise<{ isConnected: boolean; tokenLength: number }> {
  const token = await getAuthToken();
  return {
    isConnected: !!token && token.length > 0,
    tokenLength: token?.length || 0,
  };
}

/**
 * Sync Clerk token from the currently active page (must be the frontend app)
 */
export async function syncClerkTokenFromPage(): Promise<string | null> {
  try {
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    if (!tab.id) return null;

    const results = await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      func: async () => {
        if (typeof window !== 'undefined' && (window as any).Clerk?.session?.getToken) {
          try {
            const token = await (window as any).Clerk.session.getToken();
            return token || null;
          } catch {
            return null;
          }
        }
        return null;
      },
    });

    const token = results[0]?.result || null;
    if (token) {
      await chrome.storage.local.set({ clerkSessionToken: token });
    }
    return token;
  } catch {
    return null;
  }
}

// ========== API Helpers ==========

// Token is stale if synced more than 45s ago (Clerk JWT expires ~60s)
const TOKEN_MAX_AGE_MS = 45_000;

async function tryRefreshToken(): Promise<void> {
  try {
    const result = await chrome.storage.local.get(['clerkTokenSyncedAt']);
    const syncedAt = result.clerkTokenSyncedAt || 0;
    const age = Date.now() - syncedAt;

    // Only refresh if token is stale
    if (age > TOKEN_MAX_AGE_MS) {
      console.log('[CuCu API] Token stale, refreshing...', { age: Math.round(age / 1000) + 's' });
      const response = await new Promise<any>((resolve) => {
        chrome.runtime.sendMessage({ type: 'REFRESH_TOKEN' }, (res) => {
          resolve(res);
        });
      });

      // Background returned fresh token — save it
      if (response?.success && response?.token) {
        await chrome.storage.local.set({
          clerkSessionToken: response.token,
          clerkTokenSyncedAt: Date.now(),
        });
        console.log('[CuCu API] ✅ Token refreshed', response.token.length, 'chars');
      } else {
        console.log('[CuCu API] ⚠️ Token refresh failed:', response?.reason || 'unknown');
      }
    }
  } catch (err: any) {
    console.log('[CuCu API] ❌ Token refresh error:', err?.message);
  }
}

async function apiHeaders(): Promise<Record<string, string>> {
  // Try to refresh token if stale
  await tryRefreshToken();

  const token = await getAuthToken();
  const headers: Record<string, string> = { 'Content-Type': 'application/json' };
  if (token) {
    headers['Authorization'] = `Bearer ${token}`;
  }
  return headers;
}

// ========== Memo CRUD ==========

export interface CreateMemoRequest {
  content: string;
  tags?: string[];
  visibility?: string;
}

export interface MemoResponse {
  id: string;
  content: string;
  tags: string[];
  created_at: string;
}

export async function createMemo(data: CreateMemoRequest): Promise<MemoResponse> {
  const apiBase = await getApiBase();
  const headers = await apiHeaders();

  const response = await fetch(`${apiBase}/memos`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      content: data.content,
      tags: data.tags || [],
      visibility: data.visibility || 'PRIVATE',
    }),
  });

  if (!response.ok) {
    await response.text(); // consume body
    throw new Error(`Failed to create memo (${response.status})`);
  }

  const result = await response.json();
  return result;
}

/**
 * Create a memo using a direct token (from Clerk getToken())
 * Bypasses storage-based token reading — always uses fresh JWT
 */
export async function createMemoWithToken(token: string, data: CreateMemoRequest): Promise<MemoResponse> {
  const apiBase = await getApiBase();

  const response = await fetch(`${apiBase}/memos`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`,
    },
    body: JSON.stringify({
      content: data.content,
      tags: data.tags || [],
      visibility: data.visibility || 'PRIVATE',
    }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Failed to save (${response.status}): ${errorText.substring(0, 100)}`);
  }

  return await response.json();
}

// ========== Tags ==========

export async function fetchTags(): Promise<string[]> {
  try {
    const apiBase = await getApiBase();
    const headers = await apiHeaders();

    const response = await fetch(`${apiBase}/memos`, { headers });
    if (!response.ok) return [];

    const memos: any[] = await response.json();

    // Extract unique tags from all memos
    const tagSet = new Set<string>();
    for (const memo of memos) {
      if (Array.isArray(memo.tags)) {
        memo.tags.forEach((t: string) => tagSet.add(t));
      }
      // Also parse #tag from content
      const hashTags = (memo.content || '').match(/#([a-zA-Z0-9_\u00C0-\u024F\u1E00-\u1EFF]+)/g);
      if (hashTags) {
        hashTags.forEach((t: string) => tagSet.add(t.slice(1))); // remove #
      }
    }

    return Array.from(tagSet).sort();
  } catch {
    return [];
  }
}

// ========== Workspaces (Shortcuts) ==========

export interface WorkspaceItem {
  id: number;
  title: string;
  filter: string;
}

export async function fetchWorkspaces(): Promise<WorkspaceItem[]> {
  try {
    const apiBase = await getApiBase();
    const headers = await apiHeaders();

    const response = await fetch(`${apiBase}/shortcuts`, { headers });
    if (!response.ok) return [];

    const data = await response.json();
    // API may return array directly or { shortcuts: [...] }
    const shortcuts = Array.isArray(data) ? data : data.shortcuts || [];
    return shortcuts.map((s: any) => ({
      id: s.id,
      title: s.title || s.name || `Workspace ${s.id}`,
      filter: s.filter || '',
    }));
  } catch {
    return [];
  }
}

// ========== Connection Test ==========

export async function testConnection(): Promise<{ ok: boolean; message: string }> {
  try {
    const apiBase = await getApiBase();
    const headers = await apiHeaders();

    const response = await fetch(`${apiBase}/memos`, {
      method: 'GET',
      headers,
      signal: AbortSignal.timeout(5000),
    });

    if (response.ok) {
      return { ok: true, message: 'Connected successfully!' };
    } else {
      return { ok: false, message: `Server error: ${response.status}` };
    }
  } catch (error) {
    return { ok: false, message: `Cannot reach server: ${(error as Error).message}` };
  }
}

// ========== Recently Used Tags (local) ==========

export async function getRecentTags(): Promise<string[]> {
  const result = await chrome.storage.local.get(['recentTags']);
  return result.recentTags || [];
}

export async function saveRecentTags(tags: string[]): Promise<void> {
  // Keep only last 20 unique tags
  const unique = [...new Set(tags)].slice(0, 20);
  await chrome.storage.local.set({ recentTags: unique });
}
