package pt.utl.ist.scripts.process.updateData;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import net.sourceforge.fenixedu.domain.ExecutionYear;
import net.sourceforge.fenixedu.domain.StudentCurricularPlan;
import net.sourceforge.fenixedu.domain.administrativeOffice.AdministrativeOffice;
import net.sourceforge.fenixedu.domain.student.Registration;
import net.sourceforge.fenixedu.domain.student.Student;
import net.sourceforge.fenixedu.domain.student.registrationStates.RegistrationState.RegistrationStateCreator;
import net.sourceforge.fenixedu.domain.student.registrationStates.RegistrationStateType;
import net.sourceforge.fenixedu.domain.studentCurriculum.CurriculumGroup;
import net.sourceforge.fenixedu.domain.studentCurriculum.CurriculumModule;
import net.sourceforge.fenixedu.domain.studentCurriculum.ExtraCurriculumGroup;

import org.joda.time.LocalDate;

import pt.ist.bennu.core.domain.User;
import pt.ist.bennu.core.security.Authenticate;
import pt.utl.ist.scripts.commons.AtomicScript;
import pt.utl.ist.scripts.dataTransferObject.IFileLine;
import pt.utl.ist.scripts.utils.DataLoaderFromFile;

public class UpdateFlunkedState extends AtomicScript {

    static private final String EXECUTION_YEAR_NAME = "2012/2013";
    static int count = 0;

    public static void main(String[] args) {
        processWriteTransaction(new UpdateFlunkedState());
        System.exit(0);
    }

    @Override
    protected void run() throws Exception {
//        final IUserView mock =
//                new Authenticate().mock(User.readUserByUserUId("ist24616").getPerson(), "https://fenix.ist.utl.pt");
//        pt.ist.fenixWebFramework.security.UserView.setUser(mock);

        Authenticate.setUser(User.findByUsername("ist24616"));
        final Collection<FlunkedStudent> flunkedStudents =
                new DataLoaderFromFile().load(FlunkedStudent.class, IMPORT_DIR_PATH + "/lista_prescritos_10_09_2012.csv");

        for (final FlunkedStudent flunkedStudent : flunkedStudents) {

            final Student student = Student.readStudentByNumber(Integer.valueOf(flunkedStudent.getStudentNumber()));
            if (student == null) {
                logger.info("Can't find student -> " + flunkedStudent.getStudentNumber());
                continue;
            }

            processStudent(student);
        }
        logger.info("Modified: " + count);
    }

    private void processStudent(final Student student) {
        logger.info("Process Student -> " + student.getNumber());

        final List<Registration> transitionRegistrations = student.getTransitionRegistrations();
        if (!transitionRegistrations.isEmpty()) {
            for (Registration registration : transitionRegistrations) {
                deleteRegistration(registration);
            }
        }

        final Set<Registration> activeRegistrations = getActiveRegistrations(student);
        if (activeRegistrations.size() != 1) {
            logger.info("Student: " + student.getNumber() + " has more than one active registration in degree admin office");
            throw new RuntimeException();
        } else {
            if (!activeRegistrations.iterator().next().getEnrolments(ExecutionYear.readCurrentExecutionYear()).isEmpty()) {
                logger.info("Student: " + student.getNumber() + " has already enrolments this year");
                return;
            }
        }

        count++;
        changeToFlunkedState(activeRegistrations.iterator().next());

        logger.info("*************************************");
    }

    private Set<Registration> getActiveRegistrations(final Student student) {
        final Set<Registration> result = new HashSet<Registration>();
        for (final Registration registration : student.getRegistrationsFor(AdministrativeOffice.readDegreeAdministrativeOffice())) {
            if (registration.isActive() && registration.isBolonha() && !registration.getDegreeType().isEmpty()) {
                result.add(registration);
            }
        }
        return result;
    }

    private void deleteRegistration(Registration registration) {
        logger.info("Delete Transitions Registration For " + registration.getDegree().getName());
        if (registration == null || !registration.isTransition()) {
            throw new RuntimeException("error.trying.to.delete.invalid.registration");
        }

        for (; registration.hasAnyStudentCurricularPlans();) {
            final StudentCurricularPlan studentCurricularPlan = registration.getStudentCurricularPlans().iterator().next();
            if (!studentCurricularPlan.isBolonhaDegree()) {
                throw new RuntimeException("What?");
            }

            deleteCurriculumModules(studentCurricularPlan.getRoot());
            removeEmptyGroups(studentCurricularPlan.getRoot());

            final ExtraCurriculumGroup extraCurriculumGroup = studentCurricularPlan.getExtraCurriculumGroup();
            if (extraCurriculumGroup != null) {
                extraCurriculumGroup.deleteRecursive();
            }
            if (studentCurricularPlan.hasRoot()) {
                studentCurricularPlan.getRoot().delete();
            }
            studentCurricularPlan.delete();
        }

        registration.delete();

    }

    protected void deleteCurriculumModules(final CurriculumModule curriculumModule) {

        if (curriculumModule == null) {
            return;
        }

        if (!curriculumModule.isLeaf()) {
            final CurriculumGroup curriculumGroup = (CurriculumGroup) curriculumModule;
            for (final CurriculumModule each : curriculumGroup.getCurriculumModules()) {
                deleteCurriculumModules(each);
            }
        } else if (curriculumModule.isDismissal()) {
            curriculumModule.delete();
        } else {
            throw new RuntimeException("error.in.transition.state.can.only.remove.groups.and.dismissals");
        }
    }

    protected void removeEmptyGroups(final CurriculumGroup curriculumGroup) {

        if (curriculumGroup == null) {
            return;
        }

        for (final CurriculumModule curriculumModule : curriculumGroup.getCurriculumModules()) {
            if (!curriculumModule.isLeaf()) {
                removeEmptyChildGroups((CurriculumGroup) curriculumModule);
            }
        }
    }

    private void removeEmptyChildGroups(final CurriculumGroup curriculumGroup) {

        for (final CurriculumModule curriculumModule : curriculumGroup.getCurriculumModules()) {
            if (!curriculumModule.isLeaf()) {
                removeEmptyChildGroups((CurriculumGroup) curriculumModule);
            }
        }

        if (!curriculumGroup.hasAnyCurriculumModules() && !curriculumGroup.isRoot() && !curriculumGroup.isExtraCurriculum()) {
            curriculumGroup.deleteRecursive();
        }
    }

    private void changeToFlunkedState(final Registration registration) {
        logger.info("Change to Flunk State Registration -> " + registration.getDegreeCurricularPlanName());

        if (registration.getActiveStateType() != RegistrationStateType.FLUNKED) {

            final ExecutionYear executionYear = ExecutionYear.readExecutionYearByName(EXECUTION_YEAR_NAME);

            LocalDate date = new LocalDate();
            if (!executionYear.containsDate(date)) {
                date = executionYear.getBeginDateYearMonthDay().toLocalDate().plusDays(1);
            }

            RegistrationStateCreator
                    .createState(registration, null, date.toDateTimeAtStartOfDay(), RegistrationStateType.FLUNKED);
        }
    }

    public static class FlunkedStudent implements IFileLine {

        public Integer studentNumber;

        private final String LINE_SEPARATOR = "\t";

        @Override
        public boolean fillWithFileLineData(String dataLine) {
            String[] line = dataLine.split(LINE_SEPARATOR);

            if (line.length < 1) {
                System.out.println("Invalid line, ignoring it.");
                return false;
            }

            studentNumber = Integer.valueOf(line[0].trim());

            return true;
        }

        @Override
        public String getUniqueKey() {
            return null;
        }

        public Integer getStudentNumber() {
            return studentNumber;
        }

    }

}
