#include "GestorDatos.h"

GestorDatos::GestorDatos(const QString& archivoDatos) : archivoDatos(archivoDatos) {}

GestorDatos::~GestorDatos() {}

// --- Métodos de Guardado (Modificado) ---
bool GestorDatos::guardarDatos(const Usuario* usuario, const QList<Semestre*>& semestres,
                               const QList<Ramo*>& ramos, const QList<ActividadAcademica*>& eventos, // ¡ActividadAcademica!
                               const QList<ActividadDiaria*>& actividadesDiarias) {
    QJsonObject rootObject;

    if (usuario) { rootObject["usuario"] = usuarioToJson(usuario); }
    rootObject["semestres"] = semestresToJson(semestres);
    rootObject["ramos"] = ramosToJson(ramos);
    //rootObject["eventos"] = eventosToJson(eventos); // ¡ActividadAcademica!
    rootObject["actividadesDiarias"] = actividadesDiariasToJson(actividadesDiarias);

    QJsonDocument doc(rootObject);
    QFile file(archivoDatos);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
        qWarning() << "No se pudo abrir el archivo para escribir:" << file.errorString();
        return false;
    }
    file.write(doc.toJson(QJsonDocument::Indented));
    file.close();
    qDebug() << "Datos guardados exitosamente en" << archivoDatos;
    return true;
}

QJsonObject GestorDatos::usuarioToJson(const Usuario* usuario) const {
    if (!usuario) { // <--- ¡Asegúrate de esta comprobación!
        qWarning() << "Advertencia: Intentando serializar un usuario NULO.";
        return QJsonObject(); // Retorna un objeto JSON vacío si el usuario es nulo
    }
    QJsonObject obj;
    obj["idUsuario"] = usuario->getIdUsuario();
    obj["nombre"] = usuario->getNombre();
    obj["email"] = usuario->getEmail();
    obj["carrera"] = usuario->getCarrera();
    obj["totalCreditosAprobados"] = usuario->getTotalCreditosAprobados();
    return obj;
}

QJsonArray GestorDatos::ramosToJson(const QList<Ramo*>& ramos) const {
    QJsonArray array;
    for (const Ramo* ramo : ramos) {
        QJsonObject obj;
        obj["idRamo"] = ramo->getIdRamo();
        obj["nombre"] = ramo->getNombre();
        obj["codigo"] = ramo->getCodigo();
        obj["creditos"] = ramo->getCreditos();
        obj["semestreAsociadoId"] = ramo->getSemestreAsociado() ? ramo->getSemestreAsociado()->getIdSemestre() : 0;
        obj["profesor"] = ramo->getProfesor();
        obj["colorAsociado"] = ramo->getColorAsociado().name();

        // Guardar Actividades Evaluativas (ahora son ActividadAcademica*)
        QJsonArray actividadesEvalArray;

        for (ActividadAcademica* act : ramo->getActividadesEvaluativas()) { // ¡ActividadAcademica!
            qDebug() << "Serializando actividad evaluativa:" << act->getNombre();
            actividadesEvalArray.append(actividadAcademicaToJson(act)); // ¡actividadAcademicaToJson!
        }
        obj["actividadesEvaluativas"] = actividadesEvalArray;

        array.append(obj);
    }
    return array;
}

QJsonArray GestorDatos::eventosToJson(const QList<ActividadAcademica*>& eventos) const { // ¡ActividadAcademica!
    QJsonArray array;
    for (const ActividadAcademica* evento : eventos) { // ¡ActividadAcademica!
        array.append(actividadAcademicaToJson(evento)); // ¡actividadAcademicaToJson!
    }
    return array;
}

QJsonObject GestorDatos::actividadAcademicaToJson(const ActividadAcademica* actividad) const {
    QJsonObject obj;
    obj["id"] = actividad->getId();
    obj["nombre"] = actividad->getNombre();
    obj["descripcion"] = actividad->getDescripcion();
    obj["fechaInicio"] = actividad->getFechaInicio().toString(Qt::ISODate);
    obj["fechaFin"] = actividad->getFechaFin().toString(Qt::ISODate);
    obj["ramoAsociadoId"] = actividad->getRamoAsociado() ? actividad->getRamoAsociado()->getIdRamo() : 0;

    obj["ponderacion"] = actividad->getPonderacion();
    obj["notaObtenida"] = actividad->getNotaObtenida();
    obj["estaCompletada"] = actividad->estaCompletada();
    obj["estimacionHoras"] = actividad->getEstimacionHoras(); // <-- ¡Guardar la estimación de horas!

    obj["tipoActividad"] = actividad->obtenerTipoActividad();

    // Atributos específicos de clases derivadas
   if (const Proyecto* proyecto = dynamic_cast<const Proyecto*>(actividad)) {
        QJsonArray miembrosArray;
        for(const QString& miembro : proyecto->getMiembrosEquipo()) {
            miembrosArray.append(miembro);
        }
        obj["miembrosEquipo"] = miembrosArray;
        QJsonArray hitosArray;
        for(const Hito* hito : proyecto->getHitos()) {
            hitosArray.append(hitosToJson({const_cast<Hito*>(hito)}).first().toObject());
        }
        obj["hitos"] = hitosArray;
    } else if (const Certamen* certamen = dynamic_cast<const Certamen*>(actividad)) {
        obj["sala"] = certamen->getSala();
        obj["tipoCertamen"] = certamen->getTipoCertamen();
    }
    return obj;
}


QJsonArray GestorDatos::hitosToJson(const QList<Hito*>& hitos) const {
    QJsonArray array;
    for (const Hito* hito : hitos) {
        QJsonObject obj;
        obj["idHito"] = hito->getIdHito();
        obj["nombreHito"] = hito->getNombreHito();
        obj["fechaHito"] = hito->getFechaHito().toString(Qt::ISODate);
        obj["completado"] = hito->isCompletado();
        array.append(obj);
    }
    return array;
}



// ¡Nuevos métodos para serializar/deserializar Semestres!
QJsonArray GestorDatos::semestresToJson(const QList<Semestre*>& semestres) const {
    QJsonArray array;
    for (const Semestre* semestre : semestres) {
        QJsonObject obj;
        obj["idSemestre"] = semestre->getIdSemestre();
        obj["nombre"] = semestre->getNombre();
        obj["fechaInicio"] = semestre->getFechaInicio().toString(Qt::ISODate);
        obj["fechaFin"] = semestre->getFechaFin().toString(Qt::ISODate);
        array.append(obj);
    }
    return array;
}
// cargarDatos
bool GestorDatos::cargarDatos(Usuario*& usuario, QList<Semestre*>& semestres,
                              QList<Ramo*>& ramos, QList<ActividadAcademica*>& eventos, // ¡ActividadAcademica!
                              QList<ActividadDiaria*>& actividadesDiarias) {
    QFile file(archivoDatos);
    if (!file.exists()) {
        qDebug() << "El archivo de datos no existe. Se iniciará con datos vacíos.";
        return true;
    }
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "No se pudo abrir el archivo para leer:" << file.errorString();
        return false;
    }

    QByteArray jsonData = file.readAll();
    file.close();

    QJsonDocument doc = QJsonDocument::fromJson(jsonData);
    if (doc.isNull()) {
        qWarning() << "Error al parsear el JSON del archivo.";
        return false;
    }

    QJsonObject rootObject = doc.object();

    // Cargar Usuario
    if (rootObject.contains("usuario") && rootObject["usuario"].isObject()) {
        usuario = jsonToUsuario(rootObject["usuario"].toObject());
    }

    // Cargar Semestres primero
    if (rootObject.contains("semestres") && rootObject["semestres"].isArray()) {
        QJsonArray semestresArray = rootObject["semestres"].toArray();
        for (const QJsonValue& value : semestresArray) {
            if (value.isObject()) { semestres.append(jsonToSemestre(value.toObject())); }
        }
    }

    // ¡NUEVA LÓGICA CLAVE! Cargar Ramos Y sus actividadesEvaluativas anidadas en el mismo bucle.
    // Todas las ActividadAcademica* de 'actividadesEvaluativas' de los ramos se añadirán a la lista GLOBAL 'eventos'.
    if (rootObject.contains("ramos") && rootObject["ramos"].isArray()) {
        QJsonArray ramosArray = rootObject["ramos"].toArray();
        for (const QJsonValue& ramoValue : ramosArray) { // Renombrado a ramoValue para evitar conflicto
            if (ramoValue.isObject()) {
                // 1. Cargar el Ramo
                Ramo* ramo = jsonToRamo(ramoValue.toObject(), semestres); // Carga el ramo
                if (ramo) {
                    ramos.append(ramo); // Añadir el ramo a la lista de ramos

                    // 2. Cargar las actividadesEvaluativas ANIDADAS dentro de este ramo
                    if (ramoValue.toObject().contains("actividadesEvaluativas") && ramoValue.toObject()["actividadesEvaluativas"].isArray()) {
                        QJsonArray actividadesEvalArray = ramoValue.toObject()["actividadesEvaluativas"].toArray();
                        for (const QJsonValue& actValue : actividadesEvalArray) {
                            if (actValue.isObject()) {
                                // Deserializa la ActividadAcademica (Tarea, Proyecto, Certamen o Genérica)
                                // Pasamos 'ramos' para resolver el 'ramoAsociadoId' si la actividad ya lo tiene.
                                ActividadAcademica* act = jsonToActividadAcademica(actValue.toObject(), ramos);
                                if (act) {
                                    qDebug() << "Cargando actividad evaluativa:" << act->getNombre();
                                    // ¡IMPORTANTE! Asegurarse de que el ramo asociado en la actividad es el actual
                                    // Esto es para el caso en que el ramo se está cargando, y la actividad lo referencia.
                                    act->setRamoAsociado(ramo); // Asegurar que apunta a este ramo cargado
                                    // Y lo principal: ¡Añadir la ActividadAcademica a la lista GLOBAL de eventos!
                                    eventos.append(act);

                                    // Y el ramo también necesita un puntero a esta actividad en su lista interna `actividadesEvaluativas`.
                                    // Esto no crea un nuevo objeto, solo copia el puntero.

                                    ramo->agregarActividadEvaluativa(act);

                                }
                            }
                        }
                    }
                }
            }
        }
    }

    // ¡IMPORTANTE! ELIMINADA LA SECCIÓN DE CARGA GLOBAL DE "eventos" SI TU JSON NO LA TIENE.
    // Si tu JSON *solo* tiene actividades evaluativas ANIDADAS en ramos, esta sección
    // causaría un error o se saldría si no existe la clave "eventos" global o si está vacía.
    // Si tu JSON *sí* tiene un array "eventos" global para otros tipos de eventos (no evaluativos del ramo),
    // entonces deberíamos mantener una versión adaptada de este
    // DADO TU JSON DE EJEMPLO QUE MUESTRA "eventos": [], lo eliminamos para evitar problemas.
    /*
    if (rootObject.contains("eventos") && rootObject["eventos"].isArray()) {
        QJsonArray eventosArray = rootObject["eventos"].toArray();
        for (const QJsonValue& value : eventsArray) {
            if (value.isObject()) {
                ActividadAcademica* act = jsonToActividadAcademica(value.toObject(), ramos);
                if (act) {
                    // Si esta actividad tiene ramoAsociadoId, asegúrate de que el ramo esté cargado y el puntero se establezca.
                    // Ya se hace dentro de jsonToActividadAcademica
                    eventos.append(act);
                }
            }
        }
    }
    */



    // Cargar Actividades Diarias (sin cambios)
    if (rootObject.contains("actividadesDiarias") && rootObject["actividadesDiarias"].isArray()) {
        QJsonArray actividadesDiariasArray = rootObject["actividadesDiarias"].toArray();
        for (const QJsonValue& value : actividadesDiariasArray) {
            if (value.isObject()) { actividadesDiarias.append(jsonToActividadDiaria(value.toObject(), ramos)); }
        }
    }

    qDebug() << "Datos cargados exitosamente desde" << archivoDatos;
    return true;
}


Usuario* GestorDatos::jsonToUsuario(const QJsonObject& json) const { /* ... sin cambios ... */
    if (json.isEmpty()) return nullptr;
    return new Usuario(json["idUsuario"].toInt(),
                       json["nombre"].toString(),
                       json["email"].toString(),
                       json["carrera"].toString(),
                       json["totalCreditosAprobados"].toInt());
}

// jsonToRamo (modificado para buscar semestre)
Ramo* GestorDatos::jsonToRamo(const QJsonObject& json, const QList<Semestre*>& semestres) const {
    if (json.isEmpty()) return nullptr;
    int semestreAsociadoId = json["semestreAsociadoId"].toInt();
    Semestre* semestre = findSemestreById(semestreAsociadoId, semestres);


    return new Ramo(json["idRamo"].toInt(),
                    json["nombre"].toString(),
                    json["codigo"].toString(),
                    json["creditos"].toInt(),
                    semestre,
                    json["profesor"].toString(),
                    QColor(json["colorAsociado"].toString()));
}

ActividadAcademica* GestorDatos::jsonToActividadAcademica(const QJsonObject& json, const QList<Ramo*>& ramos) const {
    if (json.isEmpty()) return nullptr;

    int id = json["id"].toInt();
    QString nombre = json["nombre"].toString();
    QString descripcion = json["descripcion"].toString();
    QDate fechaInicio = QDate::fromString(json["fechaInicio"].toString(), Qt::ISODate);
    QDate fechaFin = QDate::fromString(json["fechaFin"].toString(), Qt::ISODate);
    int ramoAsociadoId = json["ramoAsociadoId"].toInt();
    Ramo* ramo = findRamoById(ramoAsociadoId, ramos);

    float ponderacion = static_cast<float>(json["ponderacion"].toDouble());
    float notaObtenida = static_cast<float>(json["notaObtenida"].toDouble(-1.0));
    bool estaCompletada = json["estaCompletada"].toBool(false);
    float estimacionHoras = static_cast<float>(json["estimacionHoras"].toDouble(0.0)); // <-- ¡Cargar la estimación de horas! (con valor por defecto 0.0)

    QString tipoActividad = json["tipoActividad"].toString();

    ActividadAcademica* nuevaActividad = nullptr;

    if (tipoActividad == "Tarea") {
        float estimacionHoras = static_cast<float>(json["estimacionHoras"].toDouble()); // Este atributo ya no va aquí, va en la base
        nuevaActividad = new Tarea(id, nombre, descripcion, fechaInicio, fechaFin, ramo,estimacionHoras,
                                   ponderacion, notaObtenida); // Pasa a la base
    } else if (tipoActividad == "Proyecto") {
        QDate fechaEntregaFinal = QDate::fromString(json["fechaEntregaFinal"].toString(), Qt::ISODate);
        QList<QString> miembrosEquipo;
        if (json["miembrosEquipo"].isArray()) {
            QJsonArray miembrosArray = json["miembrosEquipo"].toArray();
            for (const QJsonValue& val : miembrosArray) { miembrosEquipo.append(val.toString()); }
        }
        Proyecto* proyecto = new Proyecto(id, nombre, descripcion, fechaInicio, fechaFin, ramo,
                                         fechaEntregaFinal, miembrosEquipo,
                                         ponderacion, notaObtenida, estimacionHoras); // Pasa a la base
        if (json["hitos"].isArray()) {
            QJsonArray hitosArray = json["hitos"].toArray();
            for (const QJsonValue& val : hitosArray) {
                if (val.isObject()) { proyecto->agregarHito(jsonToHito(val.toObject())); }
            }
        }
        nuevaActividad = proyecto;
    } else if (tipoActividad == "Certamen") {
        QString sala = json["sala"].toString();
        QString tipoCertamen = json["tipoCertamen"].toString();
        nuevaActividad = new Certamen(id, nombre, descripcion, fechaInicio, fechaFin, ramo,
                                      sala, tipoCertamen,
                                      ponderacion, notaObtenida, estimacionHoras); // Pasa a la base
    } else if (tipoActividad == "Actividad Genérica") { // Para las actividades creadas directamente en AddRamoDialog
        nuevaActividad = new ActividadAcademica(id, nombre, descripcion, fechaInicio, fechaFin, ramo,
                                                ponderacion, notaObtenida, estimacionHoras); // Pasa estimación
    } else {
        qWarning() << "Tipo de ActividadAcademica desconocido al cargar:" << tipoActividad;
        return nullptr;
    }

    if (nuevaActividad) {
        // Restaurar el estado de completado si no se infirió por la nota
        if (!nuevaActividad->tieneNota()) {
            nuevaActividad->setCompletadaManual(estaCompletada);
        }
    }
    return nuevaActividad;
}


Hito* GestorDatos::jsonToHito(const QJsonObject& json) const { /* ... sin cambios ... */
    if (json.isEmpty()) return nullptr;
    return new Hito(json["idHito"].toInt(),
                    json["nombreHito"].toString(),
                    QDate::fromString(json["fechaHito"].toString(), Qt::ISODate),
                    json["completado"].toBool());
}


Semestre* GestorDatos::jsonToSemestre(const QJsonObject& json) const {
    if (json.isEmpty()) return nullptr;
    return new Semestre(json["idSemestre"].toInt(),
                        json["nombre"].toString(),
                        QDate::fromString(json["fechaInicio"].toString(), Qt::ISODate),
                        QDate::fromString(json["fechaFin"].toString(), Qt::ISODate));
}

Ramo* GestorDatos::findRamoById(int id, const QList<Ramo*>& ramos) const {
    for (Ramo* ramo : ramos) {
        if (ramo->getIdRamo() == id) {
            return ramo;
        }
    }
    return nullptr;
}

Semestre* GestorDatos::findSemestreById(int id, const QList<Semestre*>& semestres) const {
    for (Semestre* semestre : semestres) {
        if (semestre->getIdSemestre() == id) {
            return semestre;
        }
    }
    return nullptr;
}

QJsonArray GestorDatos::actividadesDiariasToJson(const QList<ActividadDiaria*>& actividades) const {
    QJsonArray array;
    for (const ActividadDiaria* act : actividades) {
        QJsonObject obj;
        obj["id"] = act->getId();
        obj["nombre"] = act->getNombre();
        obj["tipo"] = act->getTipo();
        obj["fecha"] = act->getFecha().toString(Qt::ISODate);
        obj["duracionMinutos"] = act->getDuracionMinutos();
        obj["ramoAsociadoId"] = act->getRamoAsociado() ? act->getRamoAsociado()->getIdRamo() : 0;
        obj["descripcion"] = act->getDescripcion();
        array.append(obj);
    }
    return array;
}

ActividadDiaria* GestorDatos::jsonToActividadDiaria(const QJsonObject& json, const QList<Ramo*>& ramos) const {
    if (json.isEmpty()) return nullptr;
    int ramoAsociadoId = json["ramoAsociadoId"].toInt();
    Ramo* ramo = findRamoById(ramoAsociadoId, ramos);

    return new ActividadDiaria(json["id"].toInt(),
                               json["nombre"].toString(),
                               static_cast<ActividadDiaria::TipoActividad>(json["tipo"].toInt()),
                               QDate::fromString(json["fecha"].toString(), Qt::ISODate),
                               json["duracionMinutos"].toInt(),
                               ramo,
                               json["descripcion"].toString());
}
