import lodash from "lodash";
import { parse } from "flags/mod.ts";
import { dirname, fromFileUrl, SEP } from "path/mod.ts";
import { networkInterfaces } from "node:os";

const REFERER = "https://w.seu.edu.cn/";
const EPORTAL_API = "https://w.seu.edu.cn:802/eportal";
const headers = {
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/117.0",
};
const PROG_PATH = Deno.args.includes("--is_compiled_binary")
    ? Deno.execPath()
    : fromFileUrl(Deno.mainModule);
const CONFIG_FILE = `${dirname(PROG_PATH)}${SEP}credentials.json`;

(async () => {

    const flags = parse(Deno.args, {
        boolean: ["login", "logout"],
        string: ["config"],
        default: { login: true, config: CONFIG_FILE }
    });

    try {
        const credentials = JSON.parse(await Deno.readTextFile(flags.config));

        if (lodash.isEmpty(credentials.username) || lodash.isEmpty(credentials.password)) {
            throw "Invalid credentials! ";
        }

        const { ipv4, ipv6, mac } = getNetIfInfo();

        if (flags.logout) {
            const LOGOUT_URL = `${EPORTAL_API}/?c=Portal&a=unbind_mac&callback=dr1004&user_account=&wlan_user_mac=${mac.replaceAll(":", "").toUpperCase()}&wlan_user_ip=${ipv4}&jsVersion=3.3.3`;
            await logout(LOGOUT_URL);
        } else {
            const LOGIN_URL = `${EPORTAL_API}/?c=Portal&a=login&callback=dr1004&login_method=1&user_account=%2C0%2C${credentials.username}&user_password=${credentials.password}&wlan_user_ip=${lodash.isEmpty(ipv4) ? '' : ipv4}&wlan_user_ipv6=${lodash.isEmpty(ipv6) ? '' : ipv6}&wlan_user_mac=000000000000&wlan_ac_ip=&wlan_ac_name=&jsVersion=3.3.3`;
            await login(LOGIN_URL);
        };

    } catch (err) {
        if (err instanceof SyntaxError) {
            exitWithError("Parse json failed!");
        } else if (err instanceof Deno.errors.NotFound) {
            exitWithError(`${CONFIG_FILE} not found!`)
        }
        else {
            console.error(`[ERROR]: ${err.toString()}`);
            exitWithError('');
        }

    }
})();

function exitWithError(errstr) {
    if (!lodash.isEmpty(errstr)) { console.error(errstr) };
    Deno.exit(1);
}

function getNetIfInfo() {
    let ipv4, ipv6, mac;
    const netIf = networkInterfaces();
    const wlanIf = netIf.wlan0 || netIf.WLAN;
    if (lodash.isEmpty(wlanIf)) {
        exitWithError("No valid wireless network interface Found!");
    }

    for (let i = 0; i < wlanIf.length; i++) {
        var alias = wlanIf[i];
        if (alias.family === 'IPv4' && !alias.internal) {
            const re = /^169.254.*/g;
            if (lodash.isEmpty(alias.address.match(re))) {
                ipv4 = alias.address;
            }
        } else if (alias.family === 'IPv6' && !alias.internal) {
            const re = /^fe80::*/g;
            if (lodash.isEmpty(alias.address.match(re))) {
                ipv6 = alias.address;
            }
        }
        mac = alias.mac;
    }
    return { ipv4, ipv6, mac };
}

async function login(LOGIN_URL) {

    const resp = await fetch(LOGIN_URL, {
        "credentials": "include",
        headers,
        "referrer": REFERER,
        "mode": "cors"
    });
    if (resp.ok) {
        const resp_text = await resp.text();
        const re = /dr1004\((.*?)\)/g;
        const match = re.exec(resp_text);
        if (lodash.isEmpty(match) || match.length <= 1) {
            throw "No match in response!";
        }
        const parsedData = JSON.parse(match[1]);
        if (parsedData.result == 1) {
            console.log("Login success!");
        } else if (parsedData.result == 0 && (parsedData.ret_code == 1 || parsedData.ret_code == 2)) {
            console.log("Already logged in!")
        } else {
            throw `Login failed.${lodash.isEmpty(parsedData.msg) ? '' : `\nServer responds message: ${parsedData.msg}`}`;
        }
    }
}

async function logout(LOGOUT_URL) {
    const resp = await fetch(LOGOUT_URL, {
        "credentials": "include",
        headers,
        "referrer": REFERER,
        "mode": "cors"
    });
    if (resp.ok) {
        const resp_text = await resp.text();
        const re = /dr1004\((.*?)\)/g;
        const match = re.exec(resp_text);
        if (lodash.isEmpty(match) || match.length <= 1) {
            throw "No match in response!";
        }
        const parsedData = JSON.parse(match[1]);
        if (parsedData.result == 1) {
            console.log("Logout success!");
        } else {
            throw `Logout failed.${lodash.isEmpty(parsedData.msg) ? '' : `\nServer responds message: ${parsedData.msg}`}`;
        }
    }
}