import { Rpc } from '@entityseven/rage-fw-rpc';

const rpc = new Rpc();
let browser: BrowserMp | null = null;
let cursorVisible = true;
let adminLevel = 0;
let noclipActive = false;
let noclipCamera: CameraMp | null = null;

// Initialize CEF via standard RAGE MP event
mp.events.add('client:initCef', (cefUrl: string, isDebug: boolean) => {
    if (isDebug) {
        mp.gui.chat.push(`[Client] Initializing CEF: ${cefUrl}`);
    }

    if (browser) {
        browser.destroy();
        browser = null;
    }
    
    browser = mp.browsers.new(cefUrl);
    rpc.browser = browser;
    
    mp.gui.chat.show(false);
    mp.gui.cursor.show(true, true);
    cursorVisible = true;

    mp.players.local.freezePosition(true);
});

// Fallback: auto-link browser
mp.events.add('browserCreated', (b: BrowserMp) => {
    if (!rpc.browser) {
        rpc.browser = b;
    }
});

// ─────────────── Keybinds ───────────────────

// Backtick (`) — toggle cursor
mp.keys.bind(0xC0, true, () => {
    cursorVisible = !cursorVisible;
    mp.gui.cursor.show(cursorVisible, cursorVisible);
});

// F2 — toggle admin noclip
mp.keys.bind(0x71, true, async () => {
    if (adminLevel <= 0) return;

    try {
        const result: any = await rpc.callServer('server:admin:noclip', []);
        if (!result || !result.allowed) return;

        noclipActive = result.active;

        if (noclipActive) {
            enableNoclip();
        } else {
            disableNoclip();
        }
    } catch (err) {
        // silently fail
    }
});

// ─────────────── Noclip Logic ───────────────

let isNoClip = false;
let noClipPos: Vector3Mp | null = null;

function enableNoclip() {
    isNoClip = true;
    noClipPos = mp.players.local.position;
    
    mp.players.local.freezePosition(true);
    mp.players.local.setInvincible(true);
    mp.players.local.setVisible(false, false);
    mp.players.local.setCollision(false, false);

    mp.gui.cursor.show(false, false);
    cursorVisible = false;
}

function disableNoclip() {
    isNoClip = false;

    if (noClipPos) {
        mp.players.local.setCoordsNoOffset(noClipPos.x, noClipPos.y, noClipPos.z, false, false, false);
        noClipPos = null;
    }

    mp.players.local.freezePosition(false);
    mp.players.local.setInvincible(false);
    mp.players.local.setVisible(true, false);
    mp.players.local.setCollision(true, false);
}

// Math helpers
function getCamDirection() {
    const rot = mp.game.cam.getGameplayCamRot(2);
    const z = rot.z * (Math.PI / 180.0);
    const x = rot.x * (Math.PI / 180.0);
    const num = Math.abs(Math.cos(x));

    return new mp.Vector3(
        -Math.sin(z) * num,
        Math.cos(z) * num,
        Math.sin(x)
    );
}

mp.events.add('render', () => {
    if (!isNoClip || !noClipPos) return;

    // Fast movement using Shift
    let speed = 1.0;
    if (mp.keys.isDown(0x10)) speed = 3.0; // Shift
    if (mp.keys.isDown(0x11)) speed = 0.2; // Ctrl

    // We do NOT disable controls — we let the user look around natively
    // But we override position manually.

    const dir = getCamDirection();

    // W = Forward
    if (mp.keys.isDown(0x57)) {
        noClipPos.x += dir.x * speed;
        noClipPos.y += dir.y * speed;
        noClipPos.z += dir.z * speed;
    }
    // S = Backward
    if (mp.keys.isDown(0x53)) {
        noClipPos.x -= dir.x * speed;
        noClipPos.y -= dir.y * speed;
        noClipPos.z -= dir.z * speed;
    }

    // A = Left
    if (mp.keys.isDown(0x41)) {
        noClipPos.x += dir.y * speed;
        noClipPos.y -= dir.x * speed;
    }
    // D = Right
    if (mp.keys.isDown(0x44)) {
        noClipPos.x -= dir.y * speed;
        noClipPos.y += dir.x * speed;
    }
    // Space = Up
    if (mp.keys.isDown(0x20)) {
        noClipPos.z += speed;
    }

    // We use direct position assignment instead of setCoordsNoOffset
    // Function calls to natives in a render loop cause massive FPS drops on low-end hardware
    // because they force physics recalculations. Assignment skips some hooks.
    mp.players.local.position = noClipPos;
});

// ─────────────── Auth Handlers ──────────────

rpc.register('auth:login', (email: string, password: string) => {
    return rpc.callServer('server:auth:login', [email, password]);
});

rpc.register('auth:register', (email: string, password: string, firstName: string, lastName: string) => {
    return rpc.callServer('server:auth:register', [email, password, firstName, lastName]);
});

rpc.register('client:authSuccess', (rpName: string, level: number) => {
    adminLevel = level || 0;
    
    mp.players.local.freezePosition(false);
    mp.gui.cursor.show(false, false);
    cursorVisible = false;

    rpc.callBrowser('app:showGame', []).catch(() => {});
});

// Save credentials to RAGE:MP local storage
rpc.register('client:saveCredentials', (email: string, password: string) => {
    mp.storage.data.auth = { email, password };
    mp.storage.flush();
});

// Load saved credentials from RAGE:MP local storage
rpc.register('client:loadCredentials', () => {
    const auth = mp.storage.data.auth;
    if (auth && auth.email) {
        return { email: auth.email, password: auth.password || '' };
    }
    return null;
});

// ─────────────── Chat Handlers ──────────────

rpc.register('chat:toggleInput', (state: boolean) => {});

rpc.register('chat:sendMessage', (msg: string) => {
    rpc.callServer('server:chat:receive', [msg]);
});

rpc.register('chat:push:custom', (chatData: any) => {
    rpc.callBrowser('chat:addMessage', [chatData]);
});

mp.events.add('chat:push', (text: string) => {
    rpc.callBrowser('chat:addMessage', [{ type: 'system', text: text }]);
});

export { rpc };
