<?php
include 'k-api.php';

function db()
{
    $pdo = new PDO('sqlite:./data.db');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    return $pdo;
};

function findClosestDateSort(array $array, string $targetDate)
{
    if (empty($array) || !preg_match('/^\d{1,2}-\d{1,2}-\d{2}$/', $targetDate)) {
        return null;
    }

    $targetDateTime = DateTime::createFromFormat('d-m-y', $targetDate);
    if ($targetDateTime === false) return null;
    $targetTimestamp = $targetDateTime->getTimestamp();

    $result = null;
    $maxTimestamp = null;

    foreach ($array as $item) {
        $itemDateTime = DateTime::createFromFormat('d-m-y', $item['fromDate']);
        if ($itemDateTime === false) continue;

        $itemTimestamp = $itemDateTime->getTimestamp();
        if ($itemTimestamp <= $targetTimestamp && ($maxTimestamp === null || $itemTimestamp > $maxTimestamp)) {
            $maxTimestamp = $itemTimestamp;
            $result = $item;
        }
    }

    return $result ?: ['content' => '[]'];
}
function getDayOfWeek(string $date)
{
    $p = explode('-', $date);
    if (count($p) !== 3) throw new InvalidArgumentException('DD-MM-YY|YYYY');
    [$d, $m, $y] = $p;
    if (strlen($y) === 2) $y = '20' . $y;
    $dt = DateTime::createFromFormat('Y-m-d', "$y-$m-$d");
    if (!$dt) throw new InvalidArgumentException('Invalid date');
    return (int)$dt->format('N') - 1; // 0=Mon ... 6=Sun
}

function joinArraysByKey(array $primaryArray, array $secondaryArray, bool $doNotOverwrite = false, string $key = 'num'): array
{
    // Index secondary array once for O(1) lookups
    $indexedArray = array_column($secondaryArray, null, $key);

    $result = [];
    foreach ($primaryArray as $item) {
        if (!isset($item[$key])) {
            $result[] = $item;
            continue;
        }

        $currentKey = $item[$key];

        if (isset($indexedArray[$currentKey])) {
            // Merge based on $doNotOverwrite flag
            $result[] = $doNotOverwrite
                ? array_merge($indexedArray[$currentKey], $item)  // Primary values take precedence
                : array_merge($item, $indexedArray[$currentKey]); // Secondary values take precedence
        } else {
            $result[] = $item;
        }
    }

    return $result;
}

function FastDBReq(PDO $db, string $sql, array $params = []): array
{
    try {
        $stmt = $db->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $e) {
        throw $e;
    }
}


CreateApi(function ($api) {

    if (file_exists('./api.lock')) {
        http_response_code(500);
        $f = fopen('./api.lock', 'r') or die("server eгor");
        $fs = filesize('./api.lock');
        if ($fs == 0) {
            $cnt = "";
        } else {
            $cnt = fread($f, $fs);
        }
        fclose($f);
        die($cnt);
    }

    $api->useUpdates(db());

    $api->on('/', 'GET', function () {
        return 'dengi';
    });
    $api->on('/login', 'POST', function ($req) {
        if (!isset($req->data['classId'])) {
            $req->res->code(400);
            return "class is empty";
        }
        $classId = substr(strtoupper($req->data['classId']), 0, 10);
        // according docs (UPPERCASE, MIN 3 MAX 10)
        $db = db();
        $results = FastDBReq($db, "SELECT classid FROM classes WHERE classid = ?", [$classId]);
        
        if (count($results) != 0) {
            return $results[0];
        } else {
            $req->res->code(400);
            return "класс $classId не найден";
        }
    });
    $api->on('/get_class_user_f', 'POST', function ($req) {
        if (!isset($req->data['classId'])) {
            $req->res->code(400);
            return "";
        }
        $classId = substr(strtoupper($req->data['classId']), 0, 10);
        // according docs (UPPERCASE, MIN 3 MAX 10)
        $db = db();
        $results = FastDBReq($db, "SELECT userclassname FROM classes WHERE classid = ?", [$classId]);
        

        if (count($results) != 0) {
            return $results[0];
        } else {
            $req->res->code(400);
            return;
        }
    });
    $api->on('/get_admin_user_f', 'POST', function ($req) {
        if (!isset($req->data['adminId'])) {
            $req->res->code(400);
            return "";
        }
        $adminId = $req->data['adminId'];
        $db = db();
        $results = FastDBReq($db, "SELECT adminName FROM admins where adminid = ?", [$adminId]);
        

        if (count($results) != 0) {
            return $results[0];
        } else {
            $req->res->code(400);
            return;
        }
    });
    $api->on('/get_admin_level', 'POST', function ($req) {
        if (!isset($req->data['adminId'])) {
            $req->res->code(400);
            return "";
        }
        $adminId = $req->data['adminId'];
        $db = db();
        $results = FastDBReq($db, "SELECT adminPrems FROM admins where adminid = ?", [$adminId]);
        

        if (count($results) != 0) {
            return $results[0];
        } else {
            $req->res->code(400);
            return;
        }
    });
    $api->on('/admin_login', 'POST', function ($req) {
        if (!isset($req->data['adminId'])) {
            $req->res->code(400);
            return "admin is empty";
        }
        $adminId = $req->data['adminId'];
        $db = db();
        $results = FastDBReq($db, "SELECT adminId FROM admins WHERE adminid = ?", [$adminId]);
        

        if (count($results) != 0) {
            return $results[0];
        } else {
            $req->res->code(400);
            return "админ не найден";
        }
    });

    $api->on('/req_client_schedule', 'POST', function ($req) {
        // NOTE: THIS IS SCHEDULE FOR USER (SHOULD BE WITH CHANGES AND CALLS)
        // types = [0 - default schedule, 1 - with schedule changes, 2 - with schedule and rings]
        if (!$req->hasData(['classId', 'day'])) {
            $req->res->code(400);
            return "net dannih";
        }
        $classId = substr(strtoupper($req->data['classId']), 0, 10);
        $day = $req->data['day'];
        if (!preg_match('/\d{1,2}-\d{1,2}-(\d{2}|\d{4})$/', $day)) return "wrong date format";

        $db = db();
        $results = FastDBReq($db, "SELECT content FROM changes WHERE classId = ? AND forDay = ?", [$classId,  $day]);


        if (count($results) > 0) {
            // return only changes
            $content = json_decode($results[0]['content'], true);
            $has_full_rings = true;
            foreach ($content as $_idx => $item) {
                if (!isset($item['t-begin']) || !isset($item['t-end'])) {
                    $has_full_rings = false;
                    // optionally record $idx
                    break;
                }
            }
            if ($has_full_rings) {
                // with rings
                return ['data' => $content, 'type' => 2];
            } else {
                // without rings
                $results = FastDBReq($db, "SELECT content, fromDate FROM RINGS WHERE classId = ?", [$classId]);
                $rings = json_decode(findClosestDateSort($results, $day)['content'], true);
                $out = $content;
                if ($rings !== null) {
                    $out = joinArraysByKey($content, $rings, true);
                }
                return ['data' => $out, 'type' => 1];
            }
        } else {
            // return shedules + calls
            $dayOfWeek = getDayOfWeek($day);
            $results = FastDBReq($db, "SELECT content, fromDate FROM schedules WHERE classId = ? AND dayOfWeek = ?", [$classId,  $dayOfWeek]);
            if (count($results) > 0) {
                $out = [];
                $schedule = json_decode(findClosestDateSort($results, $day)['content'], true);
                $results = FastDBReq($db, "SELECT content, fromDate FROM RINGS WHERE classId = ?", [$classId]);
                $rings = json_decode(findClosestDateSort($results, $day)['content'], true);
                $out = $schedule;
                if ($rings !== null) {
                    $out = joinArraysByKey($schedule, $rings);
                }
                return ['data' => $out, 'type' => 0];
            } else {
                return ['data' => []];
            }
        }
    });
    $api->on('/editor_data', "POST", function ($req) {
        if (!$req->hasData(['tab', 'day', 'adminId'])) {
            $req->res->code(400);
            return "bad data";
        }
        $adminId = $req->data['adminId'];
        $db = db();
        $results = FastDBReq($db, "SELECT adminClass FROM admins WHERE adminId=?", [$adminId]);
        if (count($results) == 0) {
            $req->res->code(400);
            return "админ не найден";
            # end
        }
        $classId = $results[0]['adminClass'];
        $day = $req->data['day'];
        $tab = intval($req->data['tab']);

        $changes = null;
        $schedule = [];
        $rings = [];

        if ($tab != 2) {
            if ($tab == 0) {
                $results = FastDBReq($db, "SELECT content FROM changes WHERE classId = ? AND forDay = ?", [$classId,  $day]);
                if (count($results) > 0) {
                    $changes = json_decode($results[0]['content'], true);
                }
            }
            $dayOfWeek = getDayOfWeek($day);
            $results = FastDBReq($db, "SELECT content, fromDate FROM schedules WHERE classId = ? AND dayOfWeek = ?", [$classId,  $dayOfWeek]);
            if (count($results) > 0) {
                $schedule = json_decode(findClosestDateSort($results, $day)['content'], true);
            }
        } else {
            $results = FastDBReq($db, "SELECT content, fromDate FROM RINGS WHERE classId = ?", [$classId]);
            if (count($results) > 0) {
                $rings = json_decode(findClosestDateSort($results, $day)['content'], true);
            }
        }

        switch ($tab) {
            case 0:
                if ($changes === null) { // type check!!!
                    return $schedule;
                } else {
                    return $changes;
                }
            case 1:
                return $schedule;
            case 2:
                return $rings;
            default:
                return "tab not found dengi dengi";
        }
    });

    $api->on(
        "/editor_publish",
        "POST",
        function ($req) {
            if (!$req->hasData(['adminId', 'tab', 'day', 'newData'])) {
                $req->res->code(400);
                return "bad data";
            }
            $adminId = $req->data['adminId'];
            $db = db();
            $results = FastDBReq($db, "SELECT adminClass FROM admins WHERE adminId=?", [$adminId]);
            if (count($results) == 0) {
                $req->res->code(400);
                return "админ не найден";
                # end
            }
            $classId = $results[0]['adminClass'];
            $day = $req->data['day'];
            $tab = intval($req->data['tab']);
            $newDataRaw = $req->data['newData'];
            $newData = json_decode($newDataRaw, true);


            switch ($tab) {
                case 0: // changes
                    // first get rasp and compare
                    // if no chnages skip, else publish

                    $dayOfWeek = getDayOfWeek($day);
                    $results = FastDBReq($db, "SELECT fromDate, content FROM schedules WHERE classId = ? AND dayOfWeek = ?", [$classId,  $dayOfWeek]);
                    $publish = true;
                    if (count($results) > 0) {
                        $schedule = json_decode(findClosestDateSort($results, $day)['content'], true);
                        if ($newData === $schedule) { // position sentensive
                            $publish = false;
                        }
                    }
                    if ($publish) {
                        // removing changes for today if they are exists
                        $results = FastDBReq($db, "SELECT content FROM changes WHERE classId = ? AND forDay = ?", [$classId,  $day]);
                        if (count($results) > 0) {
                            $changes = json_decode($results[0]['content'], true);
                            if ($newData === $changes) {
                                // already present
                                return true;
                            }
                            FastDBReq($db, "DELETE FROM changes WHERE forDay = ? AND classId = ?", [$day, $classId]); // SCARY 💀💀💀
                        }
                        // creating new
                        FastDBReq($db, 'INSERT INTO changes VALUES (?, ?, ?)', [$classId, $day, $newDataRaw]);
                        return true;
                    }
                    return false;
                case 1: // rasp
                    $dayOfWeek = getDayOfWeek($day);
                    // FIXME: im to lazy to find if closest rasp are the same
                    FastDBReq($db, 'DELETE FROM schedules WHERE classId=? AND fromDate=?', [$classId, $day]);

                    FastDBReq($db, 'INSERT INTO schedules VALUES (?,?,?,?)', [$classId, $day, $dayOfWeek, $newDataRaw]);
                    return true;
                case 2: // rigns
                    //same
                    FastDBReq($db, 'DELETE FROM rings WHERE classId=? AND fromDate=?', [$classId, $day]);
                    FastDBReq($db, 'INSERT INTO rings VALUES (?,?,?)', [$classId, $day, $newDataRaw]);
                    return true;
                default:
                    return "tab not found dengi dengi";
            }
        }
    );
    $api->on('/editor_fullitems_del', "POST", function ($req) {

        // SCARY!!!!!!
        // LEVEL 10000000
        // 💀💀💀💀💀

        if (!$req->hasData(['tab', 'day', 'adminId'])) {
            $req->res->code(400);
            return "bad data";
        }
        $adminId = $req->data['adminId'];
        $db = db();
        $results = FastDBReq($db, "SELECT adminClass FROM admins WHERE adminId=?", [$adminId]);
        if (count($results) == 0) {
            $req->res->code(400);
            return "админ не найден";
            # end
        }
        $classId = $results[0]['adminClass'];
        $day = $req->data['day'];
        $tab = intval($req->data['tab']);

        switch ($tab) {
            case 0:
                FastDBReq($db, "DELETE FROM changes WHERE forDay = ? AND classId = ?", [$day, $classId]);
                break;
            case 1:
                FastDBReq($db, 'DELETE FROM schedules WHERE classId=? AND fromDate=?', [$classId, $day]);
                break;
            case 2:
                FastDBReq($db, 'DELETE FROM rings WHERE classId=? AND fromDate=?', [$classId, $day]);
                break;
            default:
                return "you saved your live because you didn't know what tabs are here";
        }
        return true;
    });
    $api->on('/update_admin', "POST", function ($req) {
        if (!$req->hasData(['adminId', 'action'])) {
            $req->res->code(400);
            return "bad data";
        }
        $adminId = $req->data['adminId'];
        $withSelf = true;
        $action = $req->data['action'];
        if (isset($req->data['person'])) {
            $withSelf = false;
        }
        $adminId = $req->data['adminId'];
        $db = db();
        $results = FastDBReq($db, "SELECT adminPrems FROM admins WHERE adminId=?", [$adminId]);
        if (count($results) == 0) {
            $req->res->code(400);
            return "админ не найден";
        }
        if (!$withSelf && !in_array($results[0]['adminPrems'], [-1, 2])) {
            $req->res->code(400);
            return 'нету прав';
        }
        if ($withSelf) {
            switch ($action) {
                case "change_name":
                    if (!isset($req->data['value'])) {
                        $req->res->code(400);
                        return "bad data";
                    }
                    $value = trim($req->data['value']);
                    if (strlen($value) == 0) {
                        $req->res->code(400);
                        return "bad data";
                    }
                    try {
                        FastDBReq($db, 'UPDATE admins SET adminName = ? WHERE adminId = ?', [$value, $adminId]);
                    } catch (PDOException) {
                        return "egor";
                    }
                    break;
            }
        } else {
            $person = $req->data['person'];
            switch ($action) {
                case "change_name":
                    if (!isset($req->data['value'])) {
                        $req->res->code(400);
                        return "bad data";
                    }
                    $value = trim($req->data['value']);
                    if (strlen($value) == 0) {
                        $req->res->code(400);
                        return "bad data";
                    }
                    try {
                        FastDBReq($db, 'UPDATE admins SET adminName = ? WHERE adminId = ?', [$value, $person]);
                    } catch (PDOException) {
                        return "egor";
                    }
                    break;
                default:
                    return "?";
            }
        }
        return true;
    });

    $api->on('/classprofile_view', "POST", function ($req) {
        if (!$req->hasData(['adminId'])) {
            $req->res->code(400);
            return "bad data";
        }
        $adminId = $req->data['adminId'];
        $db = db();
        $results = FastDBReq($db, "SELECT adminPrems, adminClass FROM admins WHERE adminId=?", [$adminId]);
        if (count($results) == 0) {
            $req->res->code(400);
            return "админ не найден";
        }
        if (!in_array($results[0]['adminPrems'], [-1, 2])) {
            $req->res->code(400);
            return "недостаточно прав";
        }

        $classId = $results[0]['adminClass'];

        $results = FastDBReq($db, "SELECT classid FROM classes WHERE classid=?", [$classId]);
        if (count($results) == 0) {
            $req->res->code(400);
            return "класса не существует";
        }
        $classData = $results[0];
        $adminsData = FastDBReq($db, "SELECT * FROM admins WHERE adminClass=?", [$classId]);


        return ["status" => 1, "class" => $classData, "admins" => $adminsData];
    });

    $api->on('/classprofile_edit', "POST", function ($req) {
        if (!$req->hasData(['adminId', 'action'])) {
            $req->res->code(400);
            return "bad data";
        }
        $adminId = $req->data['adminId'];
        $db = db();
        $results = FastDBReq($db, "SELECT adminPrems, adminClass FROM admins WHERE adminId=?", [$adminId]);
        if (count($results) == 0) {
            $req->res->code(400);
            return "админ не найден";
        }
        if (!in_array($results[0]['adminPrems'], [-1, 2])) {
            $req->res->code(400);
            return "недостаточно прав";
        }

        $classId = $results[0]['adminClass'];

        $action = $req->data['action'];

        switch ($action) {
            case 'update_name':
                try {
                    if (!isset($req->data['value'])) {
                        $req->res->code(400);
                        return "bad data";
                    }
                    $value = trim($req->data['value']);
                    if (strlen($value) == 0) {
                        $req->res->code(400);
                        return "bad data";
                    }
                    FastDBReq($db, "UPDATE classes SET userclassname = ? WHERE classid=?", [$value, $classId]);
                    return true;
                } catch (PDOException $e) {
                    error_log($e);
                    return "егор";
                }
                break;
            case 'create_admin':
                if (!isset($req->data['value'])) {
                    $req->res->code(400);
                    return "bad data";
                }
                $value = trim($req->data['value']);
                if (strlen($value) == 0 || !str_contains($value, ';')) {
                    $req->res->code(400);
                    return "bad data";
                }
                $value = explode(';', $value);
                if (sizeof($value) != 2) {
                    $req->res->code(400);
                    return 'bad data';
                }
                $n_adminId = $value[0];
                $n_adminName = base64_decode($value[1]);

                try {
                    FastDBReq($db, "INSERT INTO admins (adminId,adminName,adminClass,adminPrems) VALUES (?,?,?,?)", [$n_adminId, $n_adminName, $classId, 1]);
                    return true;
                } catch (PDOException) {
                    $req->res->code(500);
                    return "ошибка";
                }
            case "delete_admin":
                if (!isset($req->data['value'])) {
                    $req->res->code(400);
                    return "bad data";
                }
                $person = trim($req->data['value']);
                if (strlen($person) == 0) {
                    $req->res->code(400);
                    return "bad data";
                }
                FastDBReq($db, "DELETE FROM admins WHERE adminId=?", [$person]);
                return true;
            default:
                return "?";
        }
    });
});
