import { encryptString, decryptString } from './crypto.js'
import {
  clearMasterPassword,
  getCachedKey,
} from './masterKey.js'

const BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080';
let refreshPromise = null;

function getAccessToken() {
  return localStorage.getItem('access_token');
}

function getRefreshToken() {
  return localStorage.getItem('refresh_token');
}

function setTokens(accessToken, refreshToken) {
  localStorage.setItem('access_token', accessToken);
  if (refreshToken) {
    localStorage.setItem('refresh_token', refreshToken);
  }
}

function clearAuth() {
  localStorage.removeItem('access_token');
  localStorage.removeItem('refresh_token');
  localStorage.removeItem('userId');
  localStorage.removeItem('username');
  clearMasterPassword()
  window.location.href = '/login';
}

async function getDerivedKey(userId) {
  const cacheKey = String(userId || 'user')
  const cachedKey = getCachedKey(cacheKey)
  if (cachedKey) return cachedKey

  throw new Error('Master key is unavailable. Please sign in again.')
}

function isAuthRequest(path) {
  return path === '/login' || path === '/refresh';
}

function expireSession() {
  clearAuth();
  throw new Error('Session expired. Please sign in again.');
}

async function refreshAccessToken() {
  if (refreshPromise) {
    return refreshPromise;
  }

  const refreshToken = getRefreshToken();
  if (!refreshToken) {
    throw new Error('Missing refresh token');
  }

  refreshPromise = (async () => {
    const response = await fetch(`${BASE_URL}/refresh`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ refresh_token: refreshToken }),
    });

    if (!response.ok) {
      throw new Error('Token refresh failed');
    }

    const data = await response.json();
    const accessToken = data?.access_token;
    if (!accessToken) {
      throw new Error('Invalid refresh response');
    }

    setTokens(accessToken, data?.refresh_token);
    return accessToken;
  })();

  try {
    return await refreshPromise;
  } finally {
    refreshPromise = null;
  }
}

async function request(path, options = {}, retryOnUnauthorized = true) {
  const token = getAccessToken();
  const headers = {
    'Content-Type': 'application/json',
    ...(token ? { Authorization: `Bearer ${token}` } : {}),
    ...(options.headers || {}),
  };

  const response = await fetch(`${BASE_URL}${path}`, {
    ...options,
    headers,
  });

  if (response.status === 401) {
    if (!isAuthRequest(path)) {
      if (retryOnUnauthorized) {
        try {
          await refreshAccessToken();
          return request(path, options, false);
        } catch (error) {
          console.error('Token refresh failed:', error);
          expireSession();
        }
      } else {
        expireSession();
      }
    }
  }

  if (!response.ok) {
    let errorMessage = `Request failed with status ${response.status}`;
    try {
      const errorData = await response.json();
      errorMessage = errorData.message || errorData.error || errorMessage;
    } catch {
      // ignore parse errors
    }
    throw new Error(errorMessage);
  }

  if (response.status === 204) {
    return null;
  }

  return response.json();
}

// Auth
export function signup(username, password) {
  return request('/signup', {
    method: 'POST',
    body: JSON.stringify({ username, password }),
  });
}

export function login(username, password) {
  return request('/login', {
    method: 'POST',
    body: JSON.stringify({ username, password }),
  });
}

// Users
export function getUser(userId) {
  return request(`/users/${userId}`);
}

export function deleteUser(userId) {
  return request(`/users/${userId}`, { method: 'DELETE' });
}

// Vaults
export function getVaults(userId) {
  return request(`/users/${userId}/vaults`);
}

export function createVault(userId, name) {
  return request(`/users/${userId}/vaults`, {
    method: 'POST',
    body: JSON.stringify({ name }),
  });
}

export function deleteVault(userId, vaultId) {
  return request(`/users/${userId}/vaults/${vaultId}`, { method: 'DELETE' });
}

// Passwords
export async function getPasswords(userId, vaultId) {
  const data = await request(`/users/${userId}/vaults/${vaultId}/passwords`);
  const list = Array.isArray(data) ? data : []
  const encryptedEntries = list.filter(entry => entry?.password && entry?.iv)
  const key = encryptedEntries.length > 0 ? await getDerivedKey(userId) : null

  return Promise.all(
    list.map(async entry => {
      if (!entry?.password || !entry?.iv) return entry
      try {
        const decryptedPassword = await decryptString(entry.password, entry.iv, key)
        return { ...entry, password: decryptedPassword }
      } catch {
        throw new Error('Failed to decrypt passwords. Please verify your master password by signing in again.')
      }
    }),
  )
}

export async function createPassword(userId, vaultId, data) {
  const key = await getDerivedKey(userId)
  const { ciphertext, iv } = await encryptString(data.password, key)
  return request(`/users/${userId}/vaults/${vaultId}/passwords`, {
    method: 'POST',
    body: JSON.stringify({ ...data, password: ciphertext, iv }),
  });
}

export async function updatePassword(userId, vaultId, passwordId, data) {
  const key = await getDerivedKey(userId)
  const { ciphertext, iv } = await encryptString(data.password, key)
  return request(`/users/${userId}/vaults/${vaultId}/passwords/${passwordId}`, {
    method: 'PUT',
    body: JSON.stringify({ ...data, password: ciphertext, iv }),
  });
}

export function deletePassword(userId, vaultId, passwordId) {
  return request(`/users/${userId}/vaults/${vaultId}/passwords/${passwordId}`, {
    method: 'DELETE',
  });
}
