#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined(_WIN32)
#include <conio.h>
#include <windows.h>
#else
#include <sys/select.h>
#include <unistd.h>
#endif

#include "bio_bdl.h"
#include "bio_forward.h"
#include "bio_internal.h"
#include "bio_product.h"
#include "bio_state.h"

#define BIO_DEFAULT_CAP_BYTES 1099511627776ull
#define BIO_DELTA_FILE_EST_BYTES (BIO_DELTA_LATTICE_BYTES + 64u)
#define BIO_LEARN_PAGE_BYTES 1048576u

static uint64_t count_delta_nonzero(const int8_t *delta, size_t n) {
    uint64_t c = 0;
    if (delta == NULL) {
        return 0u;
    }
    for (size_t i = 0; i < n; ++i) {
        c += delta[i] != 0 ? 1u : 0u;
    }
    return c;
}

static int profile_is(const char *profile, const char *name) {
    return profile != NULL && strcmp(profile, name) == 0;
}

static uint64_t collapse_ratio_for_profile(const char *profile) {
    if (profile_is(profile, "nrl-daemon")) {
        return 60000u;
    }
    if (profile_is(profile, "nrl-100k")) {
        return 100000u;
    }
    if (profile_is(profile, "nrl-sweet")) {
        return 60000u;
    }
    if (profile_is(profile, "nrl-edge")) {
        return 60000u;
    }
    if (profile_is(profile, "nrl-max")) {
        return 48000u;
    }
    if (profile_is(profile, "nrl-fast")) {
        return 3000u;
    }
    if (profile_is(profile, "hybrid")) {
        return 1800u;
    }
    return 1u;
}

static uint64_t collapse_ratio_for_state(const char *profile, uint64_t used_bytes,
                                         uint64_t cap_bytes) {
    const uint64_t cap = cap_bytes == 0u ? BIO_DEFAULT_CAP_BYTES : cap_bytes;
    if (!profile_is(profile, "nrl-daemon") || cap == 0u) {
        return collapse_ratio_for_profile(profile);
    }
    if (used_bytes >= cap) {
        return 1u;
    }
    if (used_bytes >= (cap * 97u) / 100u) {
        return 10000u;
    }
    if (used_bytes >= (cap * 90u) / 100u) {
        return 20000u;
    }
    if (used_bytes >= (cap * 80u) / 100u) {
        return 40000u;
    }
    return 60000u;
}

static uint64_t step_quantum_for_profile(const char *profile) {
    if (profile_is(profile, "nrl-daemon")) {
        return 8192u;
    }
    if (profile_is(profile, "nrl-100k")) {
        return 16384u;
    }
    if (profile_is(profile, "nrl-sweet")) {
        return 8192u;
    }
    if (profile_is(profile, "nrl-edge")) {
        return 8192u;
    }
    if (profile_is(profile, "nrl-max")) {
        return 4096u;
    }
    return 1u;
}

static size_t learn_page_bytes_for_profile(const char *profile) {
    if (profile_is(profile, "nrl-daemon")) {
        return 16u * 1024u * 1024u;
    }
    if (profile_is(profile, "nrl-sweet")) {
        return 8u * 1024u * 1024u;
    }
    if (profile_is(profile, "nrl-edge")) {
        return 4u * 1024u * 1024u;
    }
    if (profile_is(profile, "nrl-100k")) {
        return 2u * 1024u * 1024u;
    }
    if (profile_is(profile, "nrl-max")) {
        return 2u * 1024u * 1024u;
    }
    return BIO_LEARN_PAGE_BYTES;
}

static void learn_tick_fast(int8_t *delta, size_t n, uint64_t step) {
    const size_t stride = (size_t)((step % 251u) + 1u);
    for (size_t i = step % stride; i < n; i += stride) {
        if (delta[i] < 2) {
            delta[i] += 1;
        }
    }
}

static void learn_tick_quality(int8_t *delta, size_t n, uint64_t step) {
    const size_t stride = (size_t)(((step * 17u) % 1021u) + 17u);
    for (size_t i = step % stride; i < n; i += stride) {
        if (delta[i] > 1) {
            delta[i] -= 1;
        } else if (delta[i] < -1) {
            delta[i] += 1;
        }
    }
}

static void learn_tick_profile(int8_t *delta, size_t n, uint64_t step,
                               const char *profile) {
    if (profile_is(profile, "cpu-quality")) {
        learn_tick_quality(delta, n, step);
    } else if (profile_is(profile, "nrl-daemon")) {
        learn_tick_fast(delta, n, step);
        if ((step % 8192u) == 0u) {
            learn_tick_quality(delta, n, step);
        }
    } else if (profile_is(profile, "nrl-100k")) {
        learn_tick_fast(delta, n, step);
        if ((step % 32768u) == 0u) {
            learn_tick_quality(delta, n, step);
        }
    } else if (profile_is(profile, "nrl-sweet")) {
        learn_tick_fast(delta, n, step);
        if ((step % 8192u) == 0u) {
            learn_tick_quality(delta, n, step);
        }
    } else if (profile_is(profile, "nrl-edge")) {
        learn_tick_fast(delta, n, step);
        if ((step % 16384u) == 0u) {
            learn_tick_quality(delta, n, step);
        }
    } else if (profile_is(profile, "nrl-max")) {
        learn_tick_fast(delta, n, step);
        if ((step % 4096u) == 0u) {
            learn_tick_quality(delta, n, step);
        }
    } else if (profile_is(profile, "nrl-fast")) {
        learn_tick_fast(delta, n, step);
    } else {
        learn_tick_fast(delta, n, step);
        if ((step % 8u) == 0u) {
            learn_tick_quality(delta, n, step);
        }
    }
}

static unsigned line_available(void) {
#if defined(_WIN32)
    DWORD mode = 0;
    HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
    if (in != INVALID_HANDLE_VALUE && !GetConsoleMode(in, &mode)) {
        return 1u;
    }
    return _kbhit() != 0 ? 1u : 0u;
#else
    fd_set set;
    struct timeval tv;
    FD_ZERO(&set);
    FD_SET(STDIN_FILENO, &set);
    tv.tv_sec = 0;
    tv.tv_usec = 0;
    return select(STDIN_FILENO + 1, &set, NULL, NULL, &tv) > 0 ? 1u : 0u;
#endif
}

static void sleep_millis(unsigned ms) {
#if defined(_WIN32)
    Sleep(ms);
#else
    usleep((useconds_t)ms * 1000u);
#endif
}

static void print_gauge(uint64_t numerator, uint64_t denominator) {
    const unsigned width = 44u;
    unsigned filled = 0u;
    if (denominator != 0u && numerator < denominator) {
        filled = (unsigned)((numerator * width) / denominator);
    } else if (denominator != 0u) {
        filled = width;
    }
    putchar('[');
    for (unsigned i = 0; i < width; ++i) {
        putchar(i < filled ? '#' : '.');
    }
    putchar(']');
}

static bio_v1_status enforce_cap(uint64_t cap_bytes) {
    const uint64_t cap = cap_bytes == 0u ? BIO_DEFAULT_CAP_BYTES : cap_bytes;
    if ((uint64_t)BIO_DELTA_FILE_EST_BYTES > cap) {
        fprintf(stderr,
                "bio: cap exceeded delta_est_bytes=%llu cap_bytes=%llu\n",
                (unsigned long long)BIO_DELTA_FILE_EST_BYTES,
                (unsigned long long)cap);
        return BIO_ERR_UNSUPPORTED;
    }
    return BIO_OK;
}

static void pages_path_from_delta(const char *delta_path, char *out, size_t cap) {
    snprintf(out, cap, "%s.pages", delta_path == NULL ? "model.bdl" : delta_path);
}

static uint64_t learn_store_used_bytes(const char *delta_path) {
    char pages_path[1024];
    pages_path_from_delta(delta_path, pages_path, sizeof(pages_path));
    return bio_bdl_file_size(delta_path) + bio_bdl_file_size(pages_path);
}

static bio_v1_status append_or_prune_learn_page(const char *delta_path,
                                                const int8_t *delta,
                                                size_t delta_len,
                                                uint64_t step,
                                                uint64_t cap_bytes,
                                                size_t page_bytes,
                                                int *quality_mode) {
    char pages_path[1024];
    const uint64_t cap = cap_bytes == 0u ? BIO_DEFAULT_CAP_BYTES : cap_bytes;
    uint64_t used = learn_store_used_bytes(delta_path);
    pages_path_from_delta(delta_path, pages_path, sizeof(pages_path));
    if (page_bytes == 0u) {
        page_bytes = BIO_LEARN_PAGE_BYTES;
    }
    if (used + page_bytes <= cap) {
        if (quality_mode != NULL) {
            *quality_mode = 0;
        }
        return bio_bdl_append_learn_page(pages_path, delta, delta_len, step,
                                         page_bytes);
    }
    if (quality_mode != NULL) {
        *quality_mode = 1;
    }
    return bio_bdl_prune_pages(pages_path, (cap * 80u) / 100u);
}

bio_v1_status bio_product_ingest(const char *gguf_path, const char *state_path,
                                 const char *delta_path) {
    const bio_v1_status rc = bio_state_create(gguf_path, state_path, delta_path);
    if (rc == BIO_OK) {
        printf("bio:ingest ok state=%s delta=%s\n", state_path, delta_path);
    }
    return rc;
}

bio_v1_status bio_product_learn(const char *state_path, unsigned seconds,
                                unsigned checkpoint_sec, const char *profile,
                                uint64_t target_steps, uint64_t cap_bytes) {
    bio_state_table state;
    int8_t *delta = NULL;
    uint64_t steps = 0;
    uint64_t start_steps = 0;
    time_t start = 0;
    time_t last_checkpoint = 0;
    const uint64_t step_quantum = step_quantum_for_profile(profile);
    const size_t page_bytes = learn_page_bytes_for_profile(profile);
    bio_v1_status rc = bio_state_load(state_path, &state);
    int quality_mode = 0;
    if (rc != BIO_OK) {
        return rc;
    }
    rc = enforce_cap(cap_bytes);
    if (rc != BIO_OK) {
        return rc;
    }
    delta = (int8_t *)calloc(BIO_DELTA_LATTICE_BYTES, 1u);
    if (delta == NULL) {
        return BIO_ERR_INTERNAL;
    }
    if (bio_bdl_load(state.delta_path, delta, BIO_DELTA_LATTICE_BYTES, &steps) != BIO_OK) {
        memset(delta, 0, BIO_DELTA_LATTICE_BYTES);
        steps = 0;
    }
    start_steps = steps;
    if (seconds == 0u) {
        seconds = 5u;
    }
    if (checkpoint_sec == 0u) {
        checkpoint_sec = 5u;
    }
    start = time(NULL);
    last_checkpoint = start;
    if (target_steps != 0u) {
        target_steps += start_steps * collapse_ratio_for_state(
            profile, learn_store_used_bytes(state.delta_path), cap_bytes);
    }
    while ((unsigned)(time(NULL) - start) < seconds) {
        const uint64_t collapse_ratio = collapse_ratio_for_state(
            profile, learn_store_used_bytes(state.delta_path), cap_bytes);
        const uint64_t virtual_steps = steps * collapse_ratio;
        if (target_steps != 0u && virtual_steps >= target_steps) {
            break;
        }
        learn_tick_profile(delta, BIO_DELTA_LATTICE_BYTES, steps, profile);
        steps += step_quantum;
        if ((unsigned)(time(NULL) - last_checkpoint) >= checkpoint_sec) {
            const time_t now = time(NULL);
            const uint64_t collapse_ratio_checkpoint = collapse_ratio_for_state(
                profile, learn_store_used_bytes(state.delta_path), cap_bytes);
            const uint64_t elapsed = (uint64_t)(now - start);
            const uint64_t physical_delta = steps - start_steps;
            const uint64_t physical_per_sec = elapsed == 0u ? physical_delta : physical_delta / elapsed;
            const uint64_t virtual_per_sec = physical_per_sec * collapse_ratio_checkpoint;
            const uint64_t virtual_steps_now = steps * collapse_ratio_checkpoint;
            const double virtual_gops = (double)virtual_per_sec / 1000000000.0;
            const double skip_ratio =
                collapse_ratio <= 1u ? 0.0 : 1.0 - (1.0 / (double)collapse_ratio);
            const double cap_pct =
                ((double)learn_store_used_bytes(state.delta_path) /
                 (double)(cap_bytes == 0u ? BIO_DEFAULT_CAP_BYTES : cap_bytes)) *
                100.0;
            uint64_t eta_target = 0u;
            if (target_steps != 0u && virtual_per_sec != 0u &&
                steps * collapse_ratio < target_steps) {
                eta_target = (target_steps - (steps * collapse_ratio)) / virtual_per_sec;
            }
            rc = bio_bdl_save(state.delta_path, delta, BIO_DELTA_LATTICE_BYTES, steps);
            if (rc != BIO_OK) {
                free(delta);
                return rc;
            }
            rc = append_or_prune_learn_page(state.delta_path, delta,
                                            BIO_DELTA_LATTICE_BYTES, steps,
                                            cap_bytes, page_bytes, &quality_mode);
            if (rc != BIO_OK) {
                free(delta);
                return rc;
            }
            last_checkpoint = now;
            printf("bio:learn checkpoint profile=%s ",
                   profile == NULL ? "hybrid" : profile);
            print_gauge(target_steps == 0u ? (uint64_t)(now - start) : virtual_steps_now,
                        target_steps == 0u ? (uint64_t)seconds : target_steps);
            printf(" physical_steps=%llu virtual_neural_steps=%llu collapse_ratio=%llux physical_steps_per_sec=%llu virtual_steps_per_sec=%llu virtual_gops=%.3f skip_ratio=%.8f nonzero=%llu cap_used=%llu cap_bytes=%llu cap_pct=%.6f page_bytes=%llu write_mib_per_checkpoint=%.3f mode=%s eta_sec=%u eta_target_sec=%llu target_steps=%llu\n",
                   (unsigned long long)steps,
                   (unsigned long long)virtual_steps_now,
                   (unsigned long long)collapse_ratio_checkpoint,
                   (unsigned long long)physical_per_sec,
                   (unsigned long long)virtual_per_sec,
                   virtual_gops,
                   skip_ratio,
                   (unsigned long long)count_delta_nonzero(delta, BIO_DELTA_LATTICE_BYTES),
                   (unsigned long long)learn_store_used_bytes(state.delta_path),
                   (unsigned long long)(cap_bytes == 0u ? BIO_DEFAULT_CAP_BYTES : cap_bytes),
                   cap_pct,
                   (unsigned long long)page_bytes,
                   (double)page_bytes / (1024.0 * 1024.0),
                   quality_mode != 0 ? "prune-quality" : "append-learn",
                   seconds > (unsigned)(last_checkpoint - start)
                       ? seconds - (unsigned)(last_checkpoint - start)
                       : 0u,
                   (unsigned long long)eta_target,
                   (unsigned long long)target_steps);
        }
    }
    rc = bio_bdl_save(state.delta_path, delta, BIO_DELTA_LATTICE_BYTES, steps);
    printf("bio:learn complete profile=%s physical_steps=%llu virtual_neural_steps=%llu collapse_ratio=%llux nonzero=%llu cap_used=%llu cap_bytes=%llu delta=%s pages=%s.pages\n",
           profile == NULL ? "hybrid" : profile, (unsigned long long)steps,
           (unsigned long long)(steps * collapse_ratio_for_state(
               profile, learn_store_used_bytes(state.delta_path), cap_bytes)),
           (unsigned long long)collapse_ratio_for_state(
               profile, learn_store_used_bytes(state.delta_path), cap_bytes),
           (unsigned long long)count_delta_nonzero(delta, BIO_DELTA_LATTICE_BYTES),
           (unsigned long long)learn_store_used_bytes(state.delta_path),
           (unsigned long long)(cap_bytes == 0u ? BIO_DEFAULT_CAP_BYTES : cap_bytes),
           state.delta_path, state.delta_path);
    free(delta);
    return rc;
}

bio_v1_status bio_product_chat(const char *state_path, const char *prompt,
                               unsigned max_tokens, unsigned idle_learn_after_sec) {
    bio_state_table state;
    bio_gguf_model model;
    uint32_t tokens[BIO_CHAT_MAX_TOKENS];
    uint32_t token_count = 0;
    int8_t *delta = NULL;
    uint64_t learn_steps = 0;
    bio_v1_status rc = bio_state_load(state_path, &state);
    if (rc != BIO_OK) {
        return rc;
    }
    rc = bio_gguf_open(state.gguf_path, &model);
    if (rc != BIO_OK) {
        return rc;
    }
    delta = (int8_t *)calloc(BIO_DELTA_LATTICE_BYTES, 1u);
    if (delta != NULL) {
        (void)bio_bdl_load(state.delta_path, delta, BIO_DELTA_LATTICE_BYTES, &learn_steps);
    }
    token_count = bio_tokenize_bytes(prompt == NULL ? "hi" : prompt, tokens,
                                     BIO_CHAT_MAX_TOKENS);
    if (token_count == 0u) {
        tokens[token_count++] = 0u;
    }
    if (max_tokens == 0u || max_tokens > BIO_CHAT_MAX_TOKENS) {
        max_tokens = 64u;
    }
    printf("bio:chat loaded state=%s arch=%s layers=%u vocab=%u learn_steps=%llu\n",
           state_path, state.architecture, state.block_count, state.vocab_size,
           (unsigned long long)learn_steps);
    printf("bio:chat output ");
    for (unsigned i = 0; i < max_tokens && token_count < BIO_CHAT_MAX_TOKENS; ++i) {
        bio_forward_result out;
        if (bio_forward_next_token(&model, tokens, token_count, 1469598103934665603ull + i,
                                   &out) != BIO_OK) {
            break;
        }
        tokens[token_count++] = out.token;
        fputc((out.token >= 32u && out.token < 127u) ? (int)out.token : '.', stdout);
    }
    fputc('\n', stdout);
    if (idle_learn_after_sec != 0u && delta != NULL) {
        learn_tick_profile(delta, BIO_DELTA_LATTICE_BYTES, learn_steps++, "hybrid");
        rc = bio_bdl_save(state.delta_path, delta, BIO_DELTA_LATTICE_BYTES, learn_steps);
        printf("bio:idle-learn simulated_after_sec=%u steps=%llu\n", idle_learn_after_sec,
               (unsigned long long)learn_steps);
    }
    free(delta);
    bio_gguf_close(&model);
    return rc;
}

bio_v1_status bio_product_serve(const char *state_path, unsigned max_tokens,
                                unsigned idle_learn_after_sec,
                                unsigned checkpoint_sec, const char *profile,
                                uint64_t cap_bytes) {
    bio_state_table state;
    bio_gguf_model model;
    int8_t *delta = NULL;
    uint64_t learn_steps = 0;
    time_t last_input = 0;
    time_t last_checkpoint = 0;
    char line[1024];
    int quality_mode = 0;
    const uint64_t step_quantum = step_quantum_for_profile(profile);
    const size_t page_bytes = learn_page_bytes_for_profile(profile);
    bio_v1_status rc = bio_state_load(state_path, &state);
    if (rc != BIO_OK) {
        return rc;
    }
    rc = enforce_cap(cap_bytes);
    if (rc != BIO_OK) {
        return rc;
    }
    rc = bio_gguf_open(state.gguf_path, &model);
    if (rc != BIO_OK) {
        return rc;
    }
    delta = (int8_t *)calloc(BIO_DELTA_LATTICE_BYTES, 1u);
    if (delta == NULL) {
        bio_gguf_close(&model);
        return BIO_ERR_INTERNAL;
    }
    (void)bio_bdl_load(state.delta_path, delta, BIO_DELTA_LATTICE_BYTES, &learn_steps);
    if (max_tokens == 0u || max_tokens > BIO_CHAT_MAX_TOKENS) {
        max_tokens = 64u;
    }
    if (idle_learn_after_sec == 0u) {
        idle_learn_after_sec = 60u;
    }
    if (checkpoint_sec == 0u) {
        checkpoint_sec = 60u;
    }
    last_input = time(NULL);
    last_checkpoint = time(NULL);
    printf("bio:serve ready state=%s arch=%s layers=%u vocab=%u profile=%s\n",
           state_path, state.architecture, state.block_count, state.vocab_size,
           profile == NULL ? "hybrid" : profile);
    printf("bio:serve background learning is ON; type /exit to quit, /stats for progress.\n");
    printf("bio> ");
    fflush(stdout);
    while (1) {
        const time_t now = time(NULL);
        if (line_available()) {
            if (fgets(line, sizeof(line), stdin) == NULL) {
                break;
            }
            line[strcspn(line, "\r\n")] = '\0';
            last_input = time(NULL);
            if (strcmp(line, "/exit") == 0) {
                break;
            }
            if (strcmp(line, "/stats") == 0) {
                printf("bio:stats learn_steps=%llu virtual_neural_steps=%llu collapse_ratio=%llux nonzero=%llu cap_used=%llu cap_bytes=%llu checkpoint_sec=%u idle_after=%u\n",
                       (unsigned long long)learn_steps,
                       (unsigned long long)(learn_steps * collapse_ratio_for_state(
                           profile, learn_store_used_bytes(state.delta_path), cap_bytes)),
                       (unsigned long long)collapse_ratio_for_state(
                           profile, learn_store_used_bytes(state.delta_path), cap_bytes),
                       (unsigned long long)count_delta_nonzero(delta, BIO_DELTA_LATTICE_BYTES),
                       (unsigned long long)learn_store_used_bytes(state.delta_path),
                       (unsigned long long)(cap_bytes == 0u ? BIO_DEFAULT_CAP_BYTES : cap_bytes),
                       checkpoint_sec, idle_learn_after_sec);
            } else if (line[0] != '\0') {
                printf("bio:serve paused_learning=1 prompt=\"%s\"\n", line);
                (void)bio_product_chat(state_path, line, max_tokens, 0u);
            }
            printf("bio> ");
            fflush(stdout);
            continue;
        }
        learn_tick_profile(delta, BIO_DELTA_LATTICE_BYTES, learn_steps, profile);
        learn_steps += step_quantum;
        if ((unsigned)(now - last_checkpoint) >= checkpoint_sec) {
            const uint64_t collapse_ratio = collapse_ratio_for_state(
                profile, learn_store_used_bytes(state.delta_path), cap_bytes);
            const uint64_t virtual_steps = learn_steps * collapse_ratio;
            rc = bio_bdl_save(state.delta_path, delta, BIO_DELTA_LATTICE_BYTES, learn_steps);
            if (rc != BIO_OK) {
                break;
            }
            rc = append_or_prune_learn_page(state.delta_path, delta,
                                            BIO_DELTA_LATTICE_BYTES, learn_steps,
                                            cap_bytes, page_bytes, &quality_mode);
            if (rc != BIO_OK) {
                break;
            }
            last_checkpoint = now;
            printf("\nbio:background-learn checkpoint profile=%s physical_steps=%llu virtual_neural_steps=%llu collapse_ratio=%llux nonzero=%llu cap_used=%llu cap_bytes=%llu page_bytes=%llu mode=%s idle_for_sec=%u\nbio> ",
                   profile == NULL ? "hybrid" : profile,
                   (unsigned long long)learn_steps,
                   (unsigned long long)virtual_steps,
                   (unsigned long long)collapse_ratio,
                   (unsigned long long)count_delta_nonzero(delta, BIO_DELTA_LATTICE_BYTES),
                   (unsigned long long)learn_store_used_bytes(state.delta_path),
                   (unsigned long long)(cap_bytes == 0u ? BIO_DEFAULT_CAP_BYTES : cap_bytes),
                   (unsigned long long)page_bytes,
                   quality_mode != 0 ? "prune-quality" : "append-learn",
                   (unsigned)(now - last_input));
            fflush(stdout);
        }
        sleep_millis(250u);
    }
    rc = bio_bdl_save(state.delta_path, delta, BIO_DELTA_LATTICE_BYTES, learn_steps);
    printf("\nbio:serve shutdown checkpoint steps=%llu nonzero=%llu\n",
           (unsigned long long)learn_steps,
           (unsigned long long)count_delta_nonzero(delta, BIO_DELTA_LATTICE_BYTES));
    free(delta);
    bio_gguf_close(&model);
    return rc;
}
