package eu.dnetlib.usagestats.sushilite;

import eu.dnetlib.usagestats.repos.BaseRepository;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Repository;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Repository
public class UsageReport extends BaseRepository {
    private final Logger log = Logger.getLogger(this.getClass());

    public ReportResponseWrapper getReport(String reportName, String release, String requestorId, String beginDate,
                                           String endDate, String repositoryIdentifier, String itemIdentifier,
                                           String itemDataType, String hasDoi, String granularity, String callback) {

        List<ReportItem> reportItems = new ArrayList<>();
        List<ReportException> reportExceptions = new ArrayList<>();

        if (!granularity.equalsIgnoreCase("totals") && !granularity.equalsIgnoreCase("monthly")) {
            reportExceptions.add(new ReportException("3062", "Warning", "Invalid ReportAttribute Value", "Granularity: \'" + granularity + "\' unknown. Defaulting to Monthly"));
            granularity = "Monthly";
        }

        Date beginDateParsed;
        if (!beginDate.equals("")) {
            beginDateParsed = tryParse(beginDate);
            if (beginDateParsed != null && (granularity.toLowerCase().equals("monthly") || beginDate.length() == 7)) {
                Calendar temp = Calendar.getInstance();
                temp.setTime(beginDateParsed);
                temp.set(Calendar.DAY_OF_MONTH, temp.getActualMinimum(Calendar.DAY_OF_MONTH));
                beginDateParsed = temp.getTime();
            }
        } else {
            Calendar temp = Calendar.getInstance();
            temp.add(Calendar.MONTH, -1);
            temp.set(Calendar.DAY_OF_MONTH, temp.getActualMinimum(Calendar.DAY_OF_MONTH));
            beginDateParsed = temp.getTime();
            reportExceptions.add(new ReportException("3021", "Warning", "Unspecified Date Arguments", "Begin Date set to default: " + new SimpleDateFormat("yyyy-MM-dd").format(beginDateParsed)));
        }

        Date endDateParsed;
        if (!endDate.equals("")) {
            endDateParsed = tryParse(endDate);
            if (endDateParsed != null && (granularity.toLowerCase().equals("monthly") || endDate.length() == 7)) {
                Calendar temp = Calendar.getInstance();
                temp.setTime(endDateParsed);
                temp.set(Calendar.DAY_OF_MONTH, temp.getActualMaximum(Calendar.DAY_OF_MONTH));
                endDateParsed = temp.getTime();
            }
        } else {
            Calendar temp = Calendar.getInstance();
            temp.add(Calendar.MONTH, -1);
            temp.set(Calendar.DAY_OF_MONTH, temp.getActualMaximum(Calendar.DAY_OF_MONTH));
            endDateParsed = temp.getTime();
            reportExceptions.add(new ReportException("3021", "Warning", "Unspecified Date Arguments", "End Date set to default: " + new SimpleDateFormat("yyyy-MM-dd").format(endDateParsed)));
        }
        //log.error("dates: " + beginDateParsed.toString() + " - " + endDateParsed.toString());

        if (beginDateParsed == null) {
            reportExceptions.add(new ReportException("3020", "Error", "Invalid Date Arguments", "Begin Date: " + beginDate + " is not a valid date"));
        }
        if (endDateParsed == null) {
            reportExceptions.add(new ReportException("3020", "Error", "Invalid Date Arguments", "End Date: " + endDate + " is not a valid date"));
        }
        if (beginDateParsed != null && endDateParsed != null && !beginDateParsed.before(endDateParsed)) {
            reportExceptions.add(new ReportException("3020", "Error", "Invalid Date Arguments", "BeginDate \'" + new SimpleDateFormat("yyyy-MM-dd").format(beginDateParsed) + "\' is greater than EndDate \'" + new SimpleDateFormat("yyyy-MM-dd").format(endDateParsed) + "\'"));
        }

        String repoid = "";
        if (!repositoryIdentifier.equals("")) {
            repoid = executeRepoId(repositoryIdentifier, reportName.toLowerCase());
            if (repoid.equals("-1")) {
                reportExceptions.add(new ReportException("3060", "Error", "Invalid Filter Value", "RepositoryIdentifier: " + repositoryIdentifier + " is not valid"));
            }
        }
        String itemid = "";
        if (!itemIdentifier.equals("")) {
            String[] split = itemIdentifier.split(":");
            switch (split[0].toLowerCase()) {
                case "oid":
                    itemid = itemIdentifier;
                    break;
                case "doi":
                    itemid = itemIdentifier;
                    break;
                case "openaire":
                    itemid = itemIdentifier;
                    break;
                default:
                    reportExceptions.add(new ReportException("3060", "Error", "Invalid Filter Value", "ItemIdentifier: " + itemIdentifier + " is not valid"));
                    itemid = "-1";
            }
        }
        if (itemid.equals("") && repoid.equals("") && !reportName.equalsIgnoreCase("rr1") && !reportName.equalsIgnoreCase("jr1")) {
            reportExceptions.add(new ReportException("3070", "Error", "Required Filter Missing", "ItemIdentifier or RepositoryIdentifier must be supplied"));
        }
        if (reportName.equalsIgnoreCase("ar1")) {
            if (!itemid.equals("-1") && !repoid.equals("-1") && beginDateParsed != null && endDateParsed != null && beginDateParsed.before(endDateParsed)) {
                if (!itemid.equals("")) {
                    if (itemDataType.equalsIgnoreCase("") || itemDataType.equalsIgnoreCase("article")) {
                        executeItem(reportItems, itemIdentifier, repoid, "Article", beginDateParsed, endDateParsed, granularity);
                    } else {
                        reportExceptions.add(new ReportException("3030", "Error", "No Usage Available for Requested Dates", "Service did not find any data"));
                    }
                } else if (!repoid.equals("")) {
                    executeBatchItems(reportItems, repoid, "Article", beginDateParsed, endDateParsed, granularity);
                }
                if (reportItems.isEmpty()) {
                    reportExceptions.add(new ReportException("3030", "Error", "No Usage Available for Requested Dates", "Service did not find any data"));
                }
            }
        } else if (reportName.equalsIgnoreCase("ir1")) {
            if (!itemid.equals("-1") && !repoid.equals("-1") && beginDateParsed != null && endDateParsed != null && beginDateParsed.before(endDateParsed)) {
                if (!itemid.equals("")) {
                    executeItem(reportItems, itemIdentifier, repoid, itemDataType, beginDateParsed, endDateParsed, granularity);
                } else if (!repoid.equals("")) {
                    executeBatchItems(reportItems, repoid, itemDataType, beginDateParsed, endDateParsed, granularity);
                }
                if (reportItems.isEmpty()) {
                    reportExceptions.add(new ReportException("3030", "Error", "No Usage Available for Requested Dates", "Service did not find any data"));
                }
            }
        } else if (reportName.equalsIgnoreCase("rr1")) {
            if (!repoid.equals("-1") && beginDateParsed != null && endDateParsed != null && beginDateParsed.before(endDateParsed)) {
                executeRepo(reportItems, repoid, itemDataType, beginDateParsed, endDateParsed, granularity);
            }
            if (reportItems.isEmpty()) {
                reportExceptions.add(new ReportException("3030", "Error", "No Usage Available for Requested Dates", "Service did not find any data"));
            }
        } else if (reportName.equalsIgnoreCase("jr1")) {
            if (!repoid.equals("-1") && beginDateParsed != null && endDateParsed != null && beginDateParsed.before(endDateParsed)) {
                executeJournal(reportItems, repoid, itemDataType, beginDateParsed, endDateParsed, granularity);
            }
            if (reportItems.isEmpty()) {
                reportExceptions.add(new ReportException("3030", "Error", "No Usage Available for Requested Dates", "Service did not find any data"));
            }
        }else if (reportName.equals("")) {
            reportExceptions.add(new ReportException("3050", "Error", "Report argument is missing", "You must supply a Report argument"));
        } else {
            reportExceptions.add(new ReportException("3000", "Error", "Report " + reportName + " not supported", "Supported reports: AR1, IR1, RR1"));
        }


        ReportResponse reportResponse = new ReportResponse(reportName, release, requestorId, beginDate, endDate,
                repositoryIdentifier, itemIdentifier, itemDataType, hasDoi, granularity, callback, reportItems, reportExceptions);

        return new ReportResponseWrapper(reportResponse);
    }

    private Date tryParse(String dateString) {
        try {
            if (dateString.length() == 7) {
                return new SimpleDateFormat("yyyy-MM").parse(dateString);
            } else if (dateString.length() == 10) {
                return new SimpleDateFormat("yyyy-MM-dd").parse(dateString);
            }
        } catch (Exception e) {
            log.error("ParseError: ", e);
        }
        return null;
    }


}
