/**
 * API responder for SPA requests 
 * (c)2025 Baxxon
 */

import fs from "node:fs";
import path from "node:path";
import log from "baxxon/logger";
import TextDB from "baxxon/textdb";


export default async function(bx){

    // Parse request params
    // Support both ?action=ping and /action/ping styles
    const reqAction = bx.reqURL.searchParams.get('action') || (bx.params[0] === 'action' ? bx.params[1] : null);
    const reqBody = bx.server.req.body || {}; // expected JSON payload
	
	// Force rootPath to be relative to the current site
    const dbOp = { ...reqBody.db };
    dbOp.rootPath = path.join(bx.paths.site, 'data');

	// TextDB instance needs same options as MVC
    const db = new TextDB(dbOp); 
    await db.setData(); // load records into memory

	// Response
    let resPayload = {};

    // Handle request actions
    switch(reqAction){

        case 'ping': // Check network connection
            resPayload = {success: true};
            break;

        case 'sync': // Sync whole data object
            if(reqBody.data){
                db.setDataFromObject(reqBody.data);
                await db.write();
                resPayload = {success: true, data: db.getData()};
            } else {
                resPayload = {success: false, err: "Missing data object"};
            }
            break;

        case 'load': // Load all records	

            resPayload = {success: true, data: db.getData()};
            break;

        case 'add': // Add a new record

            if(reqBody.dir && reqBody.record){
                const {_body, ...meta} = reqBody.record;
                const result = db.addRecord(reqBody.dir, meta, _body);
                if (result.success) {
                    await db.write();
                    resPayload = {success: true, recordId: result.newId, data: db.getData()};
               } else {
                    resPayload = {success: false, err: "Failed to add record"};
               }
           } else {
                resPayload = {success: false, err: "Missing dir or record"};
           }
            break;

        case 'update': // Update an existing record

            if(reqBody.recordId && reqBody.record){
                const {_body, ...meta} = reqBody.record;
                const result = db.updateRecord(reqBody.recordId, meta, _body);
                if (result.success) {
                    await db.write();
                    resPayload = {success: true, recordId: result.newId, data: db.getData()};
               } else {
                    resPayload = {success: false, err: "Failed to update record"};
               }
           } else {
                resPayload = {success: false, err: "Missing recordId or record"};
           }
            break;

        case 'delete': // Delete a record

            if(reqBody.recordId){
                const result = db.removeRecord(reqBody.recordId);
                if (result.success){
                    await db.write();
                    resPayload = {success: true, data: db.getData()};
               } else {
                    resPayload = {success: false, err: "Failed to delete record"};
               }
           } else {
                resPayload = {success: false, err: "Missing recordId"};
           }
            break;

		case 'backup':
			if(reqBody.backupPath){

				// security check: no directory traversal allowed
				if(reqBody.backupPath.includes("..")){
					resPayload = {success: false, err: "Invalid backupPath: directory traversal not allowed"};
					break;
				}

				const src = db.getRootPath();
				const baseDst = path.resolve(bx.paths.site, reqBody.backupPath);
				const dst1 = baseDst + "-1";
				const dst2 = baseDst + "-2";

				// Decide which copy to overwrite (the oldest or the one that doesn't exist)
				let target = dst1;
				try{
					const s1 = fs.existsSync(dst1) ? fs.statSync(dst1).mtimeMs : 0;
					const s2 = fs.existsSync(dst2) ? fs.statSync(dst2).mtimeMs : 0;

					if(s1 > 0 && s2 > 0){
						target = (s1 <= s2) ? dst1 : dst2;
					}else if(s1 > 0){
						target = dst2;
					}
				}catch(err){
					log.error("Rotation logic failed", err);
				}

				try{
					// Ensure destination parent directory exists
					fs.mkdirSync(path.dirname(target), {recursive: true});
					// Clean existing target before copy
					fs.rmSync(target, {recursive: true, force: true});
					fs.cpSync(src, target, {recursive: true});
					resPayload = {success: true, date: new Date().toISOString()};
				}catch(err){
					log.error("Backup failed", err);
					resPayload = {success: false, err: "Backup failed: " + err.message};
				}
			}else{
				resPayload = {success: false, err: "Missing backupPath"};
			}
			break;

		case 'getBackupStatus':
			if(reqBody.backupPath){

				// security check: no directory traversal allowed
				if(reqBody.backupPath.includes("..")){
					resPayload = {success: false, err: "Invalid backup path: directory traversal not allowed"};
					break;
				}

				const baseDst = path.resolve(bx.paths.site, reqBody.backupPath);
				const dst1 = baseDst + "-1";
				const dst2 = baseDst + "-2";

				try{
					let latest = null;
					[dst1, dst2].forEach(d=>{
						if(fs.existsSync(d)){
							const mtime = fs.statSync(d).mtime;
							if(!latest || mtime > latest) latest = mtime;
						}
					});
					resPayload = {success: true, date: latest ? latest.toISOString() : null};
				}catch(err){
					resPayload = {success: false, err: err.message};
				}
			}else{
				resPayload = {success: false, err: "Missing backupPath"};
			}
			break;

        default: // Unknown action

            resPayload = {success: false, err: "Invalid API action"};
            break;
   }

	//log.note( "Response payload", resPayload);

    bx.render.JSON(resPayload);
};
