#include "timekeeper.hpp"
#include "SD.h"

void Timekeeper::begin() {
    // Clock outputs start as unassigned (-1)
    clockRelay1 = -1;
    clockRelay2 = -1;
    clockEnabled = false;

    Serial.println("Timekeeper initialized - clock outputs not assigned");

    // Initialize RTC
    if (!rtc.begin()) {
        Serial.println("Couldn't find RTC");
        // Continue anyway, but log the error
    } else {
        Serial.println("RTC initialized successfully");

        // Check if RTC lost power
        if (!rtc.isrunning()) {
            Serial.println("RTC is NOT running! Setting time...");
            // Set to compile time as fallback
            rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
        }
    }

    // Load today's events
    loadTodaysEvents();

    // Create tasks with appropriate priorities
    // Priority 3 = Highest (clock), 2 = Medium (scheduler), 1 = Lowest (rtc)
    xTaskCreate(clockTask, "ClockTask", 2048, this, 3, &clockTaskHandle);
    xTaskCreate(schedulerTask, "SchedulerTask", 4096, this, 2, &schedulerTaskHandle);
    xTaskCreate(rtcTask, "RTCTask", 4096, this, 1, &rtcTaskHandle);

    Serial.println("Timekeeper initialized with tasks");
}

void Timekeeper::setRelayWriteFunction(void (*func)(int, int)) {
    relayWriteFunc = func;
}

void Timekeeper::setClockOutputs(int relay1, int relay2) {
    // Validate relay numbers (assuming 0-5 are valid)
    if (relay1 < 0 || relay1 > 5 || relay2 < 0 || relay2 > 5) {
        Serial.println("Invalid relay numbers! Must be 0-5");
        return;
    }

    if (relay1 == relay2) {
        Serial.println("Clock relays cannot be the same!");
        return;
    }

    clockRelay1 = relay1;
    clockRelay2 = relay2;
    clockEnabled = true;  // Enable clock functionality

    Serial.printf("Clock outputs set to: Relay %d and Relay %d - Clock ENABLED\n", relay1, relay2);
}

void Timekeeper::setTime(unsigned long timestamp) {
    if (!rtc.begin()) {
        Serial.println("RTC not available - cannot set time");
        return;
    }

    // Convert Unix timestamp to DateTime object
    DateTime newTime(timestamp);

    // Set the RTC
    rtc.adjust(newTime);

    Serial.printf("RTC time set to: %04d-%02d-%02d %02d:%02d:%02d (timestamp: %lu)\n",
                  newTime.year(), newTime.month(), newTime.day(),
                  newTime.hour(), newTime.minute(), newTime.second(),
                  timestamp);

    // Reload today's events since the date might have changed
    loadTodaysEvents();
}

unsigned long Timekeeper::getTime() {
    if (!rtc.isrunning()) {
        Serial.println("RTC not running - cannot get time");
        return 0;
    }

    DateTime now = rtc.now();
    unsigned long timestamp = now.unixtime();

    Serial.printf("Current RTC time: %04d-%02d-%02d %02d:%02d:%02d (timestamp: %lu)\n",
                  now.year(), now.month(), now.day(),
                  now.hour(), now.minute(), now.second(),
                  timestamp);

    return timestamp;
}

void Timekeeper::loadTodaysEvents() {
    // Clear existing events
    todaysEvents.clear();

    // Get current date/time from RTC
    DateTime now = rtc.now();
    if (!rtc.isrunning()) {
        Serial.println("RTC not running - cannot load events");
        return;
    }

    int currentYear = now.year();
    int currentMonth = now.month();
    int currentDay = now.day();
    int currentDayOfWeek = now.dayOfTheWeek(); // 0=Sunday, 1=Monday, etc.

    Serial.printf("Loading events for: %04d-%02d-%02d (day %d)\n",
                  currentYear, currentMonth, currentDay, currentDayOfWeek);

    // Open and parse events file
    File file = SD.open("/events/events.json");
    if (!file) {
        Serial.println("Failed to open events.json");
        return;
    }

    DynamicJsonDocument doc(8192);
    DeserializationError error = deserializeJson(doc, file);
    file.close();

    if (error) {
        Serial.print("JSON parsing failed: ");
        Serial.println(error.c_str());
        return;
    }

    JsonArray events = doc["events"];
    int eventsLoaded = 0;

    for (JsonObject event : events) {
        if (!event["enabled"].as<bool>()) {
            continue; // Skip disabled events
        }

        String type = event["type"].as<String>();
        bool shouldAdd = false;

        if (type == "single") {
            // Check if event date matches today
            String eventDateTime = event["datetime"].as<String>();
            if (isSameDate(eventDateTime, currentYear, currentMonth, currentDay)) {
                shouldAdd = true;
            }
        }
        else if (type == "weekly") {
            // Check if today's day of week is in the event's days
            JsonArray daysOfWeek = event["days_of_week"];
            for (JsonVariant dayVar : daysOfWeek) {
                int day = dayVar.as<int>();
                if (day == currentDayOfWeek) {
                    shouldAdd = true;
                    break;
                }
            }
        }
        else if (type == "monthly") {
            // Check if today's date is in the event's days of month
            JsonArray daysOfMonth = event["days_of_month"];
            for (JsonVariant dayVar : daysOfMonth) {
                int day = dayVar.as<int>();
                if (day == currentDay) {
                    shouldAdd = true;
                    break;
                }
            }
        }

        if (shouldAdd) {
            addToTodaysSchedule(event);
            eventsLoaded++;
        }
    }

    // Sort events by time
    sortEventsByTime();

    Serial.printf("Loaded %d events for today\n", eventsLoaded);
}

bool Timekeeper::isSameDate(String eventDateTime, int year, int month, int day) {
    // Parse "2025-12-25T09:00:00" format
    if (eventDateTime.length() < 10) return false;

    int eventYear = eventDateTime.substring(0, 4).toInt();
    int eventMonth = eventDateTime.substring(5, 7).toInt();
    int eventDay = eventDateTime.substring(8, 10).toInt();

    return (eventYear == year && eventMonth == month && eventDay == day);
}

void Timekeeper::addToTodaysSchedule(JsonObject event) {
    ScheduledEvent schedEvent;

    // Extract time based on event type
    if (event["type"] == "single") {
        // Extract time from datetime: "2025-12-25T09:00:00" -> "09:00:00"
        String datetime = event["datetime"].as<String>();
        if (datetime.length() >= 19) {
            schedEvent.timeStr = datetime.substring(11, 19);
        }
    } else {
        // Weekly/Monthly events have separate time field
        schedEvent.timeStr = event["time"].as<String>();
    }

    // Store the entire event data (create a copy)
    schedEvent.eventData = event;

    todaysEvents.push_back(schedEvent);

    Serial.printf("Added event '%s' at %s\n",
                  event["name"].as<String>().c_str(),
                  schedEvent.timeStr.c_str());
}

void Timekeeper::sortEventsByTime() {
    std::sort(todaysEvents.begin(), todaysEvents.end(),
              [](const ScheduledEvent& a, const ScheduledEvent& b) {
                  return a.timeStr < b.timeStr;
              });
}

String Timekeeper::getCurrentTimeString() {
    DateTime now = rtc.now();
    if (!rtc.isrunning()) {
        return "00:00:00";
    }

    char timeStr[9];
    sprintf(timeStr, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
    return String(timeStr);
}

// Static task functions
void Timekeeper::clockTask(void* parameter) {
    Timekeeper* keeper = static_cast<Timekeeper*>(parameter);

    Serial.println("Clock task started");

    while(1) {
        unsigned long now = millis();

        // Check if a minute has passed (60000ms = 60 seconds)
        if (now - keeper->lastClockPulse >= 60000) {
            keeper->updateClock();
            keeper->lastClockPulse = now;
        }

        // Check every 100ms for precision
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
}

void Timekeeper::schedulerTask(void* parameter) {
    Timekeeper* keeper = static_cast<Timekeeper*>(parameter);

    Serial.println("Scheduler task started");

    while(1) {
        keeper->checkScheduledEvents();

        // Check every second
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void Timekeeper::rtcTask(void* parameter) {
    Timekeeper* keeper = static_cast<Timekeeper*>(parameter);

    Serial.println("RTC task started");

    while(1) {
        DateTime now = keeper->rtc.now();
        if (keeper->rtc.isrunning()) {

            // Check if it's midnight - reload events for new day
            if (now.hour() == 0 && now.minute() == 0 && now.second() < 10) {
                Serial.println("Midnight detected - reloading events");
                keeper->loadTodaysEvents();
                keeper->loadNextDayEvents();
            }

            // Check hourly for maintenance tasks
            if (now.minute() == 0 && now.second() < 10) {
                Serial.printf("Hourly check at %02d:00\n", now.hour());
                // Add any hourly maintenance here:
                // - Log status
                // - Check for schedule updates
            }
        } else {
            Serial.println("Warning: RTC not running!");
        }

        // Check every 10 seconds
        vTaskDelay(10000 / portTICK_PERIOD_MS);
    }
}

void Timekeeper::updateClock() {
    // Check if clock is enabled and outputs are assigned
    if (!clockEnabled || clockRelay1 == -1 || clockRelay2 == -1) {
        return; // Silently skip if clock not configured
    }

    DateTime now = rtc.now();
    if (!rtc.isrunning()) {
        Serial.println("RTC not running - cannot update clock");
        return;
    }

    if (relayWriteFunc == nullptr) {
        Serial.println("Relay write function not set - cannot update clock");
        return;
    }

    // Alternate between the two relays
    int activeRelay = currentClockRelay ? clockRelay2 : clockRelay1;

    Serial.printf("Clock pulse: Relay %d at %02d:%02d:%02d\n",
                  activeRelay, now.hour(), now.minute(), now.second());

    // Pulse for 5 seconds
    relayWriteFunc(activeRelay, LOW);  // Assuming LOW activates relay
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    relayWriteFunc(activeRelay, HIGH); // HIGH deactivates relay

    // Switch to other relay for next minute
    currentClockRelay = !currentClockRelay;
}

void Timekeeper::checkScheduledEvents() {
    String currentTime = getCurrentTimeString();

    // Only check the seconds part for exact matching
    String currentTimeMinute = currentTime.substring(0, 5); // "HH:MM"

    for (auto& event : todaysEvents) {
        String eventTimeMinute = event.timeStr.substring(0, 5); // "HH:MM"

        if (eventTimeMinute == currentTimeMinute) {
            // Check if we haven't already triggered this event
            if (!event.triggered) {
                triggerEvent(event);
                event.triggered = true;
            }
        }

        // Reset trigger flag when we're past the minute
        if (eventTimeMinute < currentTimeMinute) {
            event.triggered = false;
        }
    }
}

void Timekeeper::triggerEvent(ScheduledEvent& event) {
    JsonObject eventData = event.eventData;

    Serial.printf("TRIGGERING EVENT: %s at %s\n",
                  eventData["name"].as<String>().c_str(),
                  event.timeStr.c_str());

    // Here you would trigger your melody playback
    // You might want to call a function from your main program
    // or send a message to another task

    // Example of what you might do:
    JsonObject melody = eventData["melody"];
    String melodyUID = melody["uid"].as<String>();
    String melodyName = melody["name"].as<String>();

    Serial.printf("Playing melody: %s (UID: %s)\n",
                  melodyName.c_str(), melodyUID.c_str());

    // TODO: Add your melody trigger code here
    // playMelody(melody);
}

void Timekeeper::loadNextDayEvents() {
    // This function would load tomorrow's events for smooth midnight transition
    // Implementation similar to loadTodaysEvents() but for tomorrow's date
    Serial.println("Pre-loading tomorrow's events...");
    // TODO: Implement if needed for smoother transitions
}
