#include "session.h"

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static int build_path(char *out, size_t out_size, const char *sid) {
    if (!session_valid_id(sid)) {
        return 0;
    }
    int n = snprintf(out, out_size, "%s/%s", SESSION_DIR, sid);
    return n > 0 && (size_t)n < out_size;
}

int session_generate_id(char *out, size_t out_size) {
    unsigned char bytes[32];
    const char hex[] = "0123456789abcdef";

    if (out_size < sizeof(bytes) * 2 + 1) {
        return 0;
    }

    FILE *fp = fopen("/dev/urandom", "rb");
    if (fp == NULL) {
        return 0;
    }
    size_t n = fread(bytes, 1, sizeof(bytes), fp);
    fclose(fp);
    if (n != sizeof(bytes)) {
        return 0;
    }

    for (size_t i = 0; i < sizeof(bytes); i++) {
        out[i * 2] = hex[bytes[i] >> 4];
        out[i * 2 + 1] = hex[bytes[i] & 0x0f];
    }
    out[sizeof(bytes) * 2] = '\0';
    return 1;
}

int session_valid_id(const char *sid) {
    if (sid == NULL || strlen(sid) != 64) {
        return 0;
    }
    for (int i = 0; sid[i] != '\0'; i++) {
        if (!isxdigit((unsigned char)sid[i])) {
            return 0;
        }
    }
    return 1;
}

int session_cookie_id(char *out, size_t out_size) {
    const char *cookie = getenv("HTTP_COOKIE");
    const char *name = "SESSION_ID=";
    size_t name_len = strlen(name);

    if (cookie == NULL) {
        return 0;
    }

    const char *p = cookie;
    while (*p != '\0') {
        while (*p == ' ' || *p == ';') {
            p++;
        }
        if (strncmp(p, name, name_len) == 0) {
            p += name_len;
            break;
        }
        p = strchr(p, ';');
        if (p == NULL) {
            return 0;
        }
    }

    size_t i = 0;
    while (p[i] != '\0' && p[i] != ';' && i + 1 < out_size) {
        out[i] = p[i];
        i++;
    }
    out[i] = '\0';

    return session_valid_id(out);
}

int session_save(const char *sid, const Session *session) {
    char path[256];
    if (!build_path(path, sizeof(path), sid)) {
        return 0;
    }

    FILE *fp = fopen(path, "w");
    if (fp == NULL) {
        return 0;
    }

    fprintf(fp, "user_id=%d\n", session->user_id);
    fprintf(fp, "username=%s\n", session->username);
    fclose(fp);
    return 1;
}

int session_load(Session *session) {
    char sid[65];
    char path[256];
    char line[256];

    memset(session, 0, sizeof(*session));

    if (!session_cookie_id(sid, sizeof(sid)) || !build_path(path, sizeof(path), sid)) {
        return 0;
    }

    FILE *fp = fopen(path, "r");
    if (fp == NULL) {
        return 0;
    }

    while (fgets(line, sizeof(line), fp) != NULL) {
        if (strncmp(line, "user_id=", 8) == 0) {
            session->user_id = atoi(line + 8);
        } else if (strncmp(line, "username=", 9) == 0) {
            strncpy(session->username, line + 9, sizeof(session->username) - 1);
            session->username[sizeof(session->username) - 1] = '\0';
            session->username[strcspn(session->username, "\r\n")] = '\0';
        }
    }

    fclose(fp);
    return session->user_id > 0 && session->username[0] != '\0';
}

int session_destroy_current(void) {
    char sid[65];
    char path[256];
    if (!session_cookie_id(sid, sizeof(sid)) || !build_path(path, sizeof(path), sid)) {
        return 0;
    }
    return unlink(path) == 0;
}

void session_set_cookie(const char *sid) {
    printf("Set-Cookie: SESSION_ID=%s; Path=/; HttpOnly; SameSite=Lax\r\n", sid);
}

void session_clear_cookie(void) {
    printf("Set-Cookie: SESSION_ID=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0\r\n");
}
