const API_BASE =
    "http://localhost:8000";



function getToken(){

    return localStorage.getItem("token");

}


function setToken(token){

    localStorage.setItem("token", token);

}


function clearToken(){

    localStorage.removeItem("token");

}



async function apiFetch(path, options = {}){

    let headers =
        options.headers
        ? { ...options.headers }
        : {};


    headers["Content-Type"] = "application/json";


    let token =
        getToken();


    if(token){

        headers["Authorization"] = "Bearer " + token;

    }


    let response =
        await fetch(
            API_BASE + path,
            {
                ...options,
                headers
            }
        );


    if(response.status === 401){

        clearToken();

        showLogin();

        throw new Error("not authenticated");

    }


    let data =
        await response.json()
        .catch(() => null);


    if(!response.ok){

        let message =
            (data && data.detail)
            ? data.detail
            : "request failed";

        throw new Error(message);

    }


    return data;

}



// ---------- auth ----------


function showLogin(){

    document.getElementById("app").style.display = "none";
    document.getElementById("login-screen").style.display = "flex";

}


function showApp(){

    document.getElementById("login-screen").style.display = "none";
    document.getElementById("app").style.display = "grid";

}


async function handleLogin(event){

    event.preventDefault();


    let username =
        document.getElementById("login-username").value;

    let password =
        document.getElementById("login-password").value;

    let errorBox =
        document.getElementById("login-error");


    errorBox.textContent = "";


    try{

        let result =
            await apiFetch(
                "/login",
                {
                    method: "POST",
                    body: JSON.stringify({ username, password })
                }
            );


        if(result.status !== "success"){

            errorBox.textContent = "invalid username or password";
            return;

        }


        setToken(result.access_token);

        showApp();

        showPage("dashboard");

    } catch(error){

        errorBox.textContent = error.message;

    }

}


async function handleRegister(event){

    event.preventDefault();


    let username =
        document.getElementById("login-username").value;

    let password =
        document.getElementById("login-password").value;

    let errorBox =
        document.getElementById("login-error");


    errorBox.textContent = "";


    try{

        await apiFetch(
            "/create-user",
            {
                method: "POST",
                body: JSON.stringify({ username, password })
            }
        );


        errorBox.style.color = "green";
        errorBox.textContent = "account created, you can log in now";

    } catch(error){

        errorBox.style.color = "";
        errorBox.textContent = error.message;

    }

}


function logout(){

    clearToken();

    showLogin();

}



// ---------- page router ----------


function showPage(page){

    let content =
        document.getElementById("content");


    document.querySelectorAll("nav a")
        .forEach(link => link.classList.remove("active"));

    let activeLink =
        document.querySelector(`nav a[data-page="${page}"]`);

    if(activeLink){

        activeLink.classList.add("active");

    }


    if(page === "dashboard"){

        loadDashboard();

    }


    if(page === "rooms"){

        loadRooms();

    }


    if(page === "events"){

        loadEvents();

    }


    if(page === "settings"){

        content.innerHTML = `
        <h1>Settings</h1>

        <div class="card">
            Account preferences.
            <br><br>
            <button onclick="logout()">Log out</button>
        </div>
        `;

    }

}



// ---------- dashboard ----------


let calendarWeekOffset = 0;

let cachedDashboardEvents = [];

let currentUserId = null;


async function loadDashboard(){

    let content =
        document.getElementById("content");


    content.innerHTML = `
    <h1>Dashboard</h1>
    <div class="card">Loading...</div>
    `;


    calendarWeekOffset = 0;


    try{

        let me =
            await apiFetch("/me");

        currentUserId = me.id;

        let roomsData =
            await apiFetch("/rooms");

        let rooms =
            roomsData.rooms || [];


        let eventsData =
            await apiFetch("/events");

        cachedDashboardEvents =
            eventsData.events || [];


        let roomsHtml =
            rooms.length
            ? rooms.map(room => `
                <div class="list-item">
                    ${room.room_name}
                </div>
            `).join("")
            : `<p class="muted">You have no rooms yet.</p>`;


        content.innerHTML = `
        <h1>Dashboard</h1>

        <div class="card">
            <h3>Welcome back, ${me.username}</h3>
            <p>
            Overview of your calendar.
            </p>
        </div>

        <div class="card" id="calendar-card">
            ${buildWeekCalendarHtml(cachedDashboardEvents, calendarWeekOffset)}
        </div>

        <div class="card">
            <h3>Your rooms</h3>
            ${roomsHtml}
        </div>

        <div class="card">
            <h3>Create a room</h3>

            <form onsubmit="handleCreateRoom(event)">

                <input
                    type="text"
                    id="create-room-name"
                    placeholder="Room name"
                    required
                >

                <input
                    type="text"
                    id="create-room-code"
                    placeholder="Invite code"
                    required
                >

                <button type="submit">Create room</button>

            </form>

            <p id="create-room-message" class="muted"></p>
        </div>

        <div class="card">
            <h3>Join a room</h3>

            <form onsubmit="handleJoinRoom(event)">

                <input
                    type="text"
                    id="join-room-code"
                    placeholder="Invite code"
                    required
                >

                <button type="submit">Join room</button>

            </form>

            <p id="join-room-message" class="muted"></p>
        </div>
        `;

    } catch(error){

        content.innerHTML = `
        <h1>Dashboard</h1>
        <div class="card">Could not load dashboard: ${error.message}</div>
        `;

    }

}


async function handleCreateRoom(event){

    event.preventDefault();


    let roomName =
        document.getElementById("create-room-name").value;

    let inviteCode =
        document.getElementById("create-room-code").value;

    let messageBox =
        document.getElementById("create-room-message");


    try{

        await apiFetch(
            "/create-room",
            {
                method: "POST",
                body: JSON.stringify({
                    room_name: roomName,
                    invite_code: inviteCode
                })
            }
        );


        messageBox.textContent = "room created";

        loadDashboard();

    } catch(error){

        messageBox.textContent = error.message;

    }

}


async function handleJoinRoom(event){

    event.preventDefault();


    let inviteCode =
        document.getElementById("join-room-code").value;

    let messageBox =
        document.getElementById("join-room-message");


    try{

        await apiFetch(
            "/join-room",
            {
                method: "POST",
                body: JSON.stringify({ invite_code: inviteCode })
            }
        );


        messageBox.textContent = "joined room";

        loadDashboard();

    } catch(error){

        messageBox.textContent = error.message;

    }

}



// ---------- calendar (week view) ----------


const CALENDAR_SLOT_HOURS = 2;
const CALENDAR_SLOT_HEIGHT = 48;

const CALENDAR_START_HOUR = 0;

const CALENDAR_END_HOUR = 24;
const PIXELS_PER_MINUTE =
    CALENDAR_SLOT_HEIGHT / (CALENDAR_SLOT_HOURS * 60);

function isEventCancelled(event){

    let status =
        (event.status || "").toLowerCase();

    return (
        status === "cancelled"
        || status === "canceled"
        || status === "declined"
    );

}


function getEventColorClass(event, currentUserId){

    let isOwnEvent =
        event.creator_id === currentUserId;

    let isPrivateOther =
        event.visibility === "private" && !isOwnEvent;


    if(isPrivateOther){

        return "event-private";

    }


    if((event.status || "").toLowerCase() === "confirmed"){

        return "event-confirmed";

    }


    return "event-busy";

}


function getWeekStart(offset){

    let today =
        new Date();

    let base =
        new Date(
            today.getFullYear(),
            today.getMonth(),
            today.getDate() + (offset * 7)
        );

    let dayOfWeek =
        base.getDay();

    let mondayDiff =
        (dayOfWeek === 0) ? -6 : (1 - dayOfWeek);


    base.setDate(base.getDate() + mondayDiff);

    return base;

}


function changeCalendarWeek(delta){

    calendarWeekOffset += delta;


    let calendarCard =
        document.getElementById("calendar-card");

    if(calendarCard){

        calendarCard.innerHTML =
            buildWeekCalendarHtml(cachedDashboardEvents, calendarWeekOffset);

    }

}


function buildWeekCalendarHtml(events, offset){

    let weekStart =
        getWeekStart(offset);

    let today =
        new Date();

    let dayNames =
        ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

    let days = [];


    for(let i = 0; i < 7; i++){

        let d =
            new Date(weekStart);

        d.setDate(weekStart.getDate() + i);

        days.push(d);

    }


    let rangeLabel =
        days[0].toLocaleDateString("default", { day: "numeric", month: "short" })
        + " - "
        + days[6].toLocaleDateString("default", { day: "numeric", month: "short", year: "numeric" });


    let totalMinutes =
        (CALENDAR_END_HOUR - CALENDAR_START_HOUR) * 60;

    let trackHeight =
        ((CALENDAR_END_HOUR - CALENDAR_START_HOUR) / CALENDAR_SLOT_HOURS)
        * CALENDAR_SLOT_HEIGHT;

    // time gutter

    let hourRows = "";


    for(
        let h = CALENDAR_START_HOUR;
        h < CALENDAR_END_HOUR;
        h += CALENDAR_SLOT_HOURS
    ){

        hourRows += `
            <div 
                class="calendar-hour-label"
                style="height:${CALENDAR_SLOT_HEIGHT}px"
            >
                ${String(h).padStart(2, "0")}:00
            </div>
        `;

    }

    // day columns

    let dayColumns =
        days.map(day => {

            let dayEvents =
                events.filter(event => {

                    if(!event.start_time || isEventCancelled(event)){

                        return false;

                    }

                    let eventStart =
                        new Date(event.start_time);

                    return (
                        eventStart.getFullYear() === day.getFullYear()
                        && eventStart.getMonth() === day.getMonth()
                        && eventStart.getDate() === day.getDate()
                    );

                })
                .sort((a, b) => new Date(a.start_time) - new Date(b.start_time));


            // assign overlap layers: an event gets the lowest layer
            // whose most recent block has already ended

            let layerEnds = [];

            let placed =
                dayEvents.map(event => {
                    function parseLocalDate(dateString){
                        let parts = dateString.split(/[- :]/);

                        return new Date(
                            parts[0],
                            parts[1] - 1,
                            parts[2],
                            parts[3],
                            parts[4]
                        );
                    }

                    let start = parseLocalDate(event.start_time);

                    let end =
                        event.end_time
                        ? parseLocalDate(event.end_time)
                        : new Date(start.getTime() + 30 * 60000);

                    let startMinutes =
                        start.getHours() * 60 + start.getMinutes();

                    let endMinutes =
                        Math.max(
                            startMinutes + 15,
                            end.getHours() * 60 + end.getMinutes()
                        );


                    let layer = 0;

                    while(layerEnds[layer] !== undefined && layerEnds[layer] > startMinutes){

                        layer++;

                    }

                    layerEnds[layer] = endMinutes;


                    return { event, startMinutes, endMinutes, layer };

                });


            let maxLayer =
                placed.reduce((m, p) => Math.max(m, p.layer), 0);


            let blocksHtml =
                placed.map(p => {

                let topPx =
                    (p.startMinutes - CALENDAR_START_HOUR * 60)
                    * PIXELS_PER_MINUTE;


                let heightPx =
                    (p.endMinutes - p.startMinutes)
                    * PIXELS_PER_MINUTE;

                    let heightPct =
                        ((p.endMinutes - p.startMinutes) / totalMinutes) * 100;

                    let leftPct =
                        p.layer * 14;

                    let widthPct =
                        100 - (maxLayer * 14);

                    let colorClass =
                        getEventColorClass(p.event, currentUserId);

                    let timeLabel =
                        new Date(p.event.start_time)
                            .toLocaleTimeString("nl-NL", {
                                hour: "2-digit",
                                minute: "2-digit",
                                hour12: false
                            });
    
                    console.log({
                        title: p.event.title,
                        startMinutes: p.startMinutes,
                        endMinutes: p.endMinutes,
                        topPx,
                        heightPx
                    });
                    return `
                        <div
                            class="calendar-event ${colorClass}"
                            style="
                                top:${topPx}px;
                                height:${heightPx}px;
                                left:${leftPct}%;
                                width:${widthPct}%;
                                z-index:${p.layer + 1};
                            "
                            onclick="showPage('events')"
                            title="${p.event.title}"
                        >
                            <span class="calendar-event-time">${timeLabel}</span>
                            <span class="calendar-event-title">${p.event.title}</span>
                        </div>
                    `;

                }).join("");


            let isToday =
                (
                    day.getFullYear() === today.getFullYear()
                    && day.getMonth() === today.getMonth()
                    && day.getDate() === today.getDate()
                );


            return `
                <div class="calendar-day-column">

                    <div class="calendar-day-header ${isToday ? "today" : ""}">
                        <div class="calendar-day-name">${dayNames[days.indexOf(day)]}</div>
                        <div class="calendar-day-number">${day.getDate()}</div>
                    </div>

                    <div class="calendar-day-track" style="height:${trackHeight}px">
                        ${blocksHtml}
                    </div>

                </div>
            `;

        }).join("");


    return `
        <div class="calendar-header">

            <button class="calendar-nav-btn" onclick="changeCalendarWeek(-1)">
                &lt;
            </button>

            <h3>${rangeLabel}</h3>

            <button class="calendar-nav-btn" onclick="changeCalendarWeek(1)">
                &gt;
            </button>

        </div>

        <div class="calendar-legend">

            <span class="legend-item">
                <span class="legend-dot event-confirmed"></span> confirmed
            </span>

            <span class="legend-item">
                <span class="legend-dot event-busy"></span> busy
            </span>

            <span class="legend-item">
                <span class="legend-dot event-private"></span> private (others)
            </span>

        </div>

        <div class="calendar-week">

            <div class="calendar-gutter">
                <div class="calendar-day-header"></div>
                <div class="calendar-hour-labels">
                    ${hourRows}
                </div>
            </div>

            <div class="calendar-days">
                ${dayColumns}
            </div>

        </div>
    `;

}



// ---------- rooms page ----------


async function loadRooms(){

    let content =
        document.getElementById("content");


    content.innerHTML = `
    <h1>Rooms</h1>
    <div class="card">Loading...</div>
    `;


    try{

        let roomsData =
            await apiFetch("/rooms");

        let rooms =
            roomsData.rooms || [];


        if(rooms.length === 0){

            content.innerHTML = `
            <h1>Rooms</h1>
            <div class="card">
                Your shared rooms appear here.
            </div>
            `;

            return;

        }


        let roomsHtml =
            rooms.map(room => `
                <div class="card">
                    <h3>${room.room_name}</h3>
                    <p class="muted">invite code: ${room.invite_code || "-"}</p>
                    <button onclick="loadRoomEvents(${room.id})">
                        View events
                    </button>
                </div>
            `).join("");


        content.innerHTML = `
        <h1>Rooms</h1>
        ${roomsHtml}
        `;

    } catch(error){

        content.innerHTML = `
        <h1>Rooms</h1>
        <div class="card">Could not load rooms: ${error.message}</div>
        `;

    }

}


async function loadRoomEvents(roomId){

    let content =
        document.getElementById("content");


    content.innerHTML = `
    <h1>Room events</h1>
    <div class="card">Loading...</div>
    `;


    try{

        let data =
            await apiFetch(`/rooms/${roomId}/events`);

        let events =
            data.events || [];


        content.innerHTML = `
        <h1>Room events</h1>
        ${renderEventCards(events)}
        `;

    } catch(error){

        content.innerHTML = `
        <h1>Room events</h1>
        <div class="card">Could not load events: ${error.message}</div>
        `;

    }

}



// ---------- events page ----------


async function loadEvents(){

    let content =
        document.getElementById("content");


    content.innerHTML = `
    <h1>Events</h1>
    <div class="card">Loading...</div>
    `;


    try{

        let data =
            await apiFetch("/events");

        let events =
            data.events || [];


        content.innerHTML = `
        <h1>Events</h1>
        ${renderEventCards(events)}
        `;

    } catch(error){

        content.innerHTML = `
        <h1>Events</h1>
        <div class="card">Could not load events: ${error.message}</div>
        `;

    }

}


function renderEventCards(events){

    if(events.length === 0){

        return `<div class="card">No events scheduled.</div>`;

    }


    return events.map(event => `

        <div class="card">

            <h3>${event.title}</h3>

            <p>${event.description || ""}</p>

            <p class="muted">
                ${formatEventTime(event.start_time, event.end_time)}
            </p>

            <p class="muted">
                status: ${event.status}
            </p>

            <div class="event-actions">

                <button onclick="handleRespond(${event.id}, 'going')">
                    Going
                </button>

                <button onclick="handleRespond(${event.id}, 'maybe')">
                    Maybe
                </button>

                <button onclick="handleRespond(${event.id}, 'declined')">
                    Declined
                </button>

            </div>

            <p
                id="event-response-${event.id}"
                class="muted"
            ></p>

        </div>

    `).join("");

}


function formatEventTime(start, end){

    if(!start){

        return "time unknown";

    }


    let startDate =
        new Date(start);

    let endDate =
        end
        ? new Date(end)
        : null;


    let startText =
        startDate.toLocaleString();

    if(!endDate){

        return startText;

    }


    return startText + " - " + endDate.toLocaleString();

}


async function handleRespond(eventId, status){

    let messageBox =
        document.getElementById(`event-response-${eventId}`);


    try{

        let result =
            await apiFetch(
                `/events/${eventId}/respond?status=${status}`,
                { method: "POST" }
            );


        messageBox.textContent =
            `you responded: ${status} (${result.going} going)`;

    } catch(error){

        messageBox.textContent = error.message;

    }

}



// ---------- theme ----------


function toggleTheme(){

    document.body.classList.toggle("dark");


    let theme =
        document.body.classList.contains("dark")
        ? "dark"
        : "light";


    localStorage.setItem(
        "theme",
        theme
    );

}



// ---------- init ----------


let savedTheme =
    localStorage.getItem("theme");


if(savedTheme === "dark"){

    document.body.classList.add("dark");

}


if(getToken()){

    showApp();

    showPage("dashboard");

} else {

    showLogin();

}