#include "rlc_turn_manager.h"
#include "rlc_components.h"
#include "rlc_items.h"
#include "rlc_log.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Action queue comparison function for qsort */
static int action_compare(const void *a, const void *b) {
    const QueuedAction *qa = (const QueuedAction *)a;
    const QueuedAction *qb = (const QueuedAction *)b;
    /* Sort by delay ascending, then priority descending */
    if (qa->delay != qb->delay) return qa->delay - qb->delay;
    return qb->priority - qa->priority;
}

/* Event queue comparison function for qsort */
static int event_compare(const void *a, const void *b) {
    const DelayedEvent *ea = (const DelayedEvent *)a;
    const DelayedEvent *eb = (const DelayedEvent *)b;
    /* Sort by turn_due ascending, then priority descending */
    /* Use comparison operators instead of subtraction to prevent overflow */
    if (ea->turn_due < eb->turn_due) return -1;
    if (ea->turn_due > eb->turn_due) return 1;
    if (eb->priority < ea->priority) return -1;
    if (eb->priority > ea->priority) return 1;
    return 0;
}

/* Action queue management */
void rlc_action_queue_init(TurnState *ts) {
    ts->action_capacity = 32;
    ts->action_count = 0;
    ts->action_queue = malloc(sizeof(QueuedAction) * ts->action_capacity);
}

void rlc_action_queue_push(TurnState *ts, QueuedAction action) {
    if (ts->action_count >= ts->action_capacity) {
        size_t new_capacity = ts->action_capacity * 2;
        QueuedAction *new_queue = realloc(ts->action_queue,
                                         sizeof(QueuedAction) * new_capacity);
        if (!new_queue) {
            RLC_LOG_ERROR("Failed to resize action queue (capacity=%zu)", ts->action_capacity);
            return;
        }
        ts->action_queue = new_queue;
        ts->action_capacity = new_capacity;
    }
    ts->action_queue[ts->action_count++] = action;
}

void rlc_action_queue_sort(TurnState *ts) {
    if (ts->action_count > 1) {
        qsort(ts->action_queue, ts->action_count,
              sizeof(QueuedAction), action_compare);
    }
}

void rlc_action_queue_clear(TurnState *ts) {
    ts->action_count = 0;
}

void rlc_action_queue_destroy(TurnState *ts) {
    if (ts->action_queue) {
        free(ts->action_queue);
        ts->action_queue = NULL;
        ts->action_count = 0;
        ts->action_capacity = 0;
    }
}

/* Event queue management */
void rlc_event_queue_init(TurnState *ts) {
    ts->event_capacity = 16;
    ts->event_count = 0;
    ts->event_queue = malloc(sizeof(DelayedEvent) * ts->event_capacity);
}

void rlc_event_queue_push(TurnState *ts, DelayedEvent event) {
    if (ts->event_count >= ts->event_capacity) {
        size_t new_capacity = ts->event_capacity * 2;
        DelayedEvent *new_queue = realloc(ts->event_queue,
                                          sizeof(DelayedEvent) * new_capacity);
        if (!new_queue) {
            RLC_LOG_ERROR("Failed to resize event queue (capacity=%zu)", ts->event_capacity);
            return;
        }
        ts->event_queue = new_queue;
        ts->event_capacity = new_capacity;
    }
    ts->event_queue[ts->event_count++] = event;
}

void rlc_event_queue_process(ecs_world_t *world, TurnState *ts) {
    if (ts->event_count == 0) return;

    /* Sort events by turn_due and priority */
    qsort(ts->event_queue, ts->event_count,
          sizeof(DelayedEvent), event_compare);

    size_t remaining = 0;

    for (size_t i = 0; i < ts->event_count; i++) {
        DelayedEvent *event = &ts->event_queue[i];

        if (event->turn_due <= ts->round_number) {
            /* Execute event */
            if (event->callback) {
                event->callback(world, event->data);
            }
            /* Free data if allocated */
            if (event->data) {
                free(event->data);
                event->data = NULL;
            }
        } else {
            /* Keep event - move to front if needed */
            if (remaining != i) {
                ts->event_queue[remaining] = *event;
            }
            remaining++;
        }
    }

    ts->event_count = remaining;
}

void rlc_event_queue_clear(TurnState *ts) {
    for (size_t i = 0; i < ts->event_count; i++) {
        if (ts->event_queue[i].data) {
            free(ts->event_queue[i].data);
            ts->event_queue[i].data = NULL;
        }
    }
    ts->event_count = 0;
}

void rlc_event_queue_destroy(TurnState *ts) {
    if (ts->event_queue) {
        rlc_event_queue_clear(ts);
        free(ts->event_queue);
        ts->event_queue = NULL;
        ts->event_capacity = 0;
    }
}

/* Turn manager initialization */
void rlc_turn_manager_init(ecs_world_t *world) {
    TurnState *ts = ecs_singleton_get_mut(world, TurnState);
    if (ts) {
        memset(ts, 0, sizeof(TurnState));
        ts->phase = PHASE_WAITING_FOR_PLAYER;
        ts->round_number = 1;
        ts->processing = false;
        ts->phase_transitioned = false;

        rlc_action_queue_init(ts);
        rlc_event_queue_init(ts);
    }
}

/* --- Message Log API Implementation --- */

const char* rlc_get_entity_display_name(ecs_world_t *world, ecs_entity_t entity) {
    if (!world || !entity) {
        return "something";
    }

    /* Check for flecs built-in name first */
    const char *name = ecs_get_name(world, entity);
    if (name && name[0] != '\0') {
        return name;
    }

    /* Check if it's the player */
    if (ecs_has(world, entity, Player)) {
        return "you";
    }

    return "something";
}

void rlc_log_message(ecs_world_t *world, int type, const char *fmt, ...) {
    if (!world || !fmt) return;

    MessageLog *log = ecs_singleton_get_mut(world, MessageLog);
    if (!log) return;

    const TurnState *ts = ecs_singleton_get(world, TurnState);
    int turn = ts ? ts->round_number : 0;

    /* Format the message */
    char formatted[128];
    va_list args;
    va_start(args, fmt);
    vsnprintf(formatted, sizeof(formatted), fmt, args);
    va_end(args);

    /* Check for stacking with the most recent message */
    if (log->message_count > 0) {
        int prev_idx = (log->write_index - 1 + MAX_LOG_MESSAGES) % MAX_LOG_MESSAGES;
        LogMessage *prev = &log->messages[prev_idx];

        /* Stack if same message on same turn */
        if ((int)prev->type == type &&
            prev->turn_number == turn &&
            strcmp(prev->formatted, formatted) == 0) {
            prev->stack_count++;
            log->updated_this_frame = true;
            return;
        }
    }

    /* Write new message */
    LogMessage *msg = &log->messages[log->write_index];
    strncpy(msg->formatted, formatted, sizeof(msg->formatted) - 1);
    msg->formatted[sizeof(msg->formatted) - 1] = '\0';
    msg->type = (LogMessageType)type;
    msg->turn_number = turn;
    msg->stack_count = 1;

    /* Advance write index (circular) */
    log->write_index = (log->write_index + 1) % MAX_LOG_MESSAGES;
    if (log->message_count < MAX_LOG_MESSAGES) {
        log->message_count++;
    }

    log->updated_this_frame = true;
}

void rlc_log_combat(ecs_world_t *world, ecs_entity_t attacker, ecs_entity_t target, int damage, bool critical) {
    if (!world) return;

    /* Cache names at call time to avoid entity recycling issues */
    const char *attacker_name = rlc_get_entity_display_name(world, attacker);
    const char *target_name = rlc_get_entity_display_name(world, target);

    /* Copy names to local buffers in case entities are deleted */
    char atk_buf[64], tgt_buf[64];
    snprintf(atk_buf, sizeof(atk_buf), "%s", attacker_name);
    snprintf(tgt_buf, sizeof(tgt_buf), "%s", target_name);

    /* Capitalize first letter for display */
    if (atk_buf[0] >= 'a' && atk_buf[0] <= 'z') {
        atk_buf[0] = atk_buf[0] - 'a' + 'A';
    }

    /* Check if target is dead (for kill message) */
    const Health *target_hp = ecs_get(world, target, Health);
    bool is_kill = target_hp && target_hp->current <= 0;

    if (is_kill) {
        rlc_log_message(world, LOG_MSG_COMBAT_KILL,
                       "%s kills %s!", atk_buf, tgt_buf);
    } else if (critical) {
        rlc_log_message(world, LOG_MSG_COMBAT_CRITICAL,
                       "%s critically hits %s for %d damage!", atk_buf, tgt_buf, damage);
    } else if (damage > 0) {
        rlc_log_message(world, LOG_MSG_COMBAT_HIT,
                       "%s hits %s for %d damage.", atk_buf, tgt_buf, damage);
    } else {
        rlc_log_message(world, LOG_MSG_COMBAT_MISS,
                       "%s misses %s.", atk_buf, tgt_buf);
    }
}

void rlc_log_combat_detailed(ecs_world_t *world, ecs_entity_t attacker, ecs_entity_t target,
                              int raw_damage, int defense, int final_damage,
                              ecs_entity_t weapon_entity) {
    if (!world) return;

    const char *attacker_name = rlc_get_entity_display_name(world, attacker);
    const char *target_name = rlc_get_entity_display_name(world, target);

    char atk_buf[64], tgt_buf[64];
    snprintf(atk_buf, sizeof(atk_buf), "%s", attacker_name);
    snprintf(tgt_buf, sizeof(tgt_buf), "%s", target_name);

    if (atk_buf[0] >= 'a' && atk_buf[0] <= 'z') {
        atk_buf[0] = atk_buf[0] - 'a' + 'A';
    }

    const Health *target_hp = ecs_get(world, target, Health);
    bool is_kill = target_hp && target_hp->current <= 0;

    if (is_kill) {
        rlc_log_message(world, LOG_MSG_COMBAT_KILL,
                       "%s kills %s!", atk_buf, tgt_buf);
    } else if (final_damage > 0) {
        /* Get weapon name for detailed message */
        const char *wpn_name = NULL;
        if (weapon_entity) {
            const Name *wn = ecs_get(world, weapon_entity, Name);
            if (wn) wpn_name = wn->str;
        }

        if (wpn_name && defense > 0) {
            rlc_log_message(world, LOG_MSG_COMBAT_HIT,
                "%s hits %s with %s: %d - %d def = %d",
                atk_buf, tgt_buf, wpn_name, raw_damage, defense, final_damage);
        } else if (wpn_name) {
            rlc_log_message(world, LOG_MSG_COMBAT_HIT,
                "%s hits %s with %s for %d damage.",
                atk_buf, tgt_buf, wpn_name, final_damage);
        } else if (defense > 0) {
            rlc_log_message(world, LOG_MSG_COMBAT_HIT,
                "%s hits %s: %d - %d def = %d",
                atk_buf, tgt_buf, raw_damage, defense, final_damage);
        } else {
            rlc_log_message(world, LOG_MSG_COMBAT_HIT,
                "%s hits %s for %d damage.",
                atk_buf, tgt_buf, final_damage);
        }
    } else {
        rlc_log_message(world, LOG_MSG_COMBAT_MISS,
                       "%s misses %s.", atk_buf, tgt_buf);
    }
}
