/**
 * API layer for Calendink Provider ESP32-S3 dashboard.
 *
 * VITE_API_BASE controls the backend URL:
 * - Development (PC): "http://<ESP32_IP>" (set in .env.development)
 * - Production (ESP32): "" (empty = relative URLs, same origin)
 */

const API_BASE = import.meta.env.VITE_API_BASE || '';
import { pendingRequests } from './stores.js';

/**
 * Wrapper around fetch that tracks the number of pending requests globally
 */
async function trackedFetch(url, options = {}) {
    pendingRequests.update(n => n + 1);
    try {
        return await fetch(url, options);
    } finally {
        pendingRequests.update(n => Math.max(0, n - 1));
    }
}

/**
 * Fetch system information from the ESP32.
 * @returns {Promise<{chip: string, freeHeap: number, uptime: number, firmware: string, connection: string}>}
 */
export async function getSystemInfo() {
    const res = await trackedFetch(`${API_BASE}/api/system/info`);
    if (!res.ok) {
        throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    }
    const data = await res.json();
    return {
        ...data,
        freeHeap: data.free_heap
    };
}

/**
 * Send a reboot command to the ESP32.
 * @returns {Promise<{message: string}>}
 */
export async function reboot() {
    const res = await trackedFetch(`${API_BASE}/api/system/reboot`, {
        method: 'POST',
    });
    if (!res.ok) {
        throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    }
    return res.json();
}

/**
 * Fetch OTA status from the ESP32.
 * @returns {Promise<{active_slot: number, active_partition: string, target_partition: string, partitions: any[], running_firmware_label: string, running_firmware_slot: number}>}
 */
export async function getOTAStatus() {
    const res = await trackedFetch(`${API_BASE}/api/ota/status`);
    if (!res.ok) {
        throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    }
    return res.json();
}

/**
 * Upload a new frontend binary image.
 * @param {File} file The binary file to upload.
 * @returns {Promise<{status: string, message: string}>}
 */
export async function uploadOTAFrontend(file) {
    const res = await trackedFetch(`${API_BASE}/api/ota/frontend`, {
        method: 'POST',
        body: file, // Send the raw file Blob/Buffer
        headers: {
            // Let the browser set Content-Type for the binary payload, 
            // or we could force application/octet-stream.
            'Content-Type': 'application/octet-stream'
        }
    });
    
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Upload failed (${res.status}): ${errorText || res.statusText}`);
    }
    
    return res.json();
}
/**
 * Upload a new firmware binary image.
 * @param {File} file The firmware binary file to upload.
 * @returns {Promise<{status: string, message: string}>}
 */
export async function uploadOTAFirmware(file) {
    const res = await trackedFetch(`${API_BASE}/api/ota/firmware`, {
        method: 'POST',
        body: file,
        headers: {
            'Content-Type': 'application/octet-stream'
        }
    });
    
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Upload failed (${res.status}): ${errorText || res.statusText}`);
    }
    
    return res.json();
}

/**
 * Upload a universal .bundle image (FW + WWW).
 * @param {File} file The bundle binary file to upload.
 * @returns {Promise<{status: string, message: string}>}
 */
export async function uploadOTABundle(file) {
    const res = await trackedFetch(`${API_BASE}/api/ota/bundle`, {
        method: 'POST',
        body: file,
        headers: {
            'Content-Type': 'application/octet-stream'
        }
    });
    
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Upload failed (${res.status}): ${errorText || res.statusText}`);
    }
    
    return res.json();
}

// ─── User Management ─────────────────────────────────────────────────────────

/**
 * Fetch all users.
 * @returns {Promise<Array<{id: number, name: string}>>}
 */
export async function getUsers() {
    const res = await trackedFetch(`${API_BASE}/api/users`);
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    return res.json();
}

/**
 * Create a new user.
 * @param {string} name
 * @returns {Promise<{id: number, name: string}>}
 */
export async function addUser(name) {
    const res = await trackedFetch(`${API_BASE}/api/users`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name })
    });
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Failed (${res.status}): ${errorText || res.statusText}`);
    }
    return res.json();
}

/**
 * Delete a user and all their tasks.
 * @param {number} id
 * @returns {Promise<{status: string}>}
 */
export async function removeUser(id) {
    const res = await trackedFetch(`${API_BASE}/api/users?id=${id}`, {
        method: 'DELETE'
    });
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Failed (${res.status}): ${errorText || res.statusText}`);
    }
    return res.json();
}

/**
 * Update a user's name.
 * @param {number} id
 * @param {string} name
 * @returns {Promise<{status: string}>}
 */
export async function updateUser(id, name) {
    const res = await trackedFetch(`${API_BASE}/api/users/update`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ id, name })
    });
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Failed (${res.status}): ${errorText || res.statusText}`);
    }
    return res.json();
}

// ─── Task Management ─────────────────────────────────────────────────────────

/**
 * Fetch tasks for a specific user.
 * @param {number} userId
 * @returns {Promise<Array<{id: number, user_id: number, title: string, due_date: number, completed: boolean}>>}
 */
export async function getTasks(userId) {
    const res = await trackedFetch(`${API_BASE}/api/tasks?user_id=${userId}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    return res.json();
}

/**
 * Fetch top 3 upcoming tasks per user (for Dashboard).
 * @returns {Promise<{users: Array<{id: number, name: string, tasks: Array}>}>}
 */
export async function getUpcomingTasks() {
    const res = await trackedFetch(`${API_BASE}/api/tasks/upcoming`);
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    return res.json();
}

/**
 * Create a new task.
 * @param {number} userId
 * @param {string} title
 * @param {number} dueDate Unix timestamp in seconds (used for non-recurrent tasks)
 * @param {number} period 0=Morning, 1=Afternoon, 2=Evening
 * @param {number} recurrence 0=None, 1-7=Day of week (1=Mon)
 * @returns {Promise<{id: number, user_id: number, title: string, due_date: number, period: number, recurrence: number, completed: boolean}>}
 */
export async function addTask(userId, title, dueDate, period = 0, recurrence = 0) {
    const res = await trackedFetch(`${API_BASE}/api/tasks`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ user_id: userId, title, due_date: dueDate, period, recurrence })
    });
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Failed (${res.status}): ${errorText || res.statusText}`);
    }
    return res.json();
}

/**
 * Update a task (partial update — only include fields you want to change).
 * @param {number} id
 * @param {Object} fields - { title?: string, due_date?: number, period?: number, recurrence?: number, completed?: boolean }
 * @returns {Promise<{status: string}>}
 */
export async function updateTask(id, fields) {
    const res = await trackedFetch(`${API_BASE}/api/tasks/update`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ id, ...fields })
    });
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Failed (${res.status}): ${errorText || res.statusText}`);
    }
    return res.json();
}

/**
 * Delete a task.
 * @param {number} id
 * @returns {Promise<{status: string}>}
 */
export async function deleteTask(id) {
    const res = await trackedFetch(`${API_BASE}/api/tasks?id=${id}`, {
        method: 'DELETE'
    });
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Failed (${res.status}): ${errorText || res.statusText}`);
    }
    return res.json();
}

// ─── Device Management ───────────────────────────────────────────────────────

/**
 * Fetch all registered devices and the default layout.
 * @returns {Promise<{default_layout: string, devices: Array<{mac: string, has_layout: boolean, xml_layout: string}>}>}
 */
export async function getDevices() {
    const res = await trackedFetch(`${API_BASE}/api/devices`);
    if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    return res.json();
}

/**
 * Register a new device.
 * @param {string} mac 
 * @returns {Promise<{status: string}>}
 */
export async function registerDevice(mac) {
    const res = await trackedFetch(`${API_BASE}/api/devices/register`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ mac })
    });
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Failed (${res.status}): ${errorText || res.statusText}`);
    }
    return res.json();
}

/**
 * Update the LVGL XML layout for a device.
 * @param {string} mac
 * @param {string} xml
 * @returns {Promise<{status: string}>}
 */
export async function updateDeviceLayout(mac, xml) {
    const res = await trackedFetch(`${API_BASE}/api/devices/layout`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ mac, xml })
    });
    if (!res.ok) {
        const errorText = await res.text();
        throw new Error(`Failed (${res.status}): ${errorText || res.statusText}`);
    }
    return res.json();
}

