package ru.yandex.autotests.directapi.model.api5.reports;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.yandex.direct.api.v5.reports.FieldEnum;

import ru.yandex.autotests.directapi.exceptions.DirectAPIException;

/**
 * Created by pavryabov on 08.06.16.
 */
public class ReportsData {

    public static final String DATE_FORMAT = "yyyy-MM-dd";
    public static final String REPORT_NAME_FORMAT = "\"%s (%s - %s)\"";
    private static final String NULL_VALUE = "--";
    private static final Pattern REPORT_HEADER_PATTERN =
            Pattern.compile("^\"?(.*)(?: \\((\\d{4}-\\d{2}-\\d{2}) - (\\d{4}-\\d{2}-\\d{2})\\))\"?$");
    private static final Pattern TOTAL_ROWS_PATTERN = Pattern.compile("^Total rows: (\\d+)$");

    private String reportHeader;

    private String reportHeaderName;

    private String reportHeaderStartDate = null;

    private String reportHeaderEndDate = null;

    private String columnsHeaders;

    private List<String> columnsHeadersList;

    private List<ReportsLine> reportsLines;

    private String totalRows;

    private long totalRowsNumber;

    public ReportsData(String tsv, List<FieldEnum> fieldNames) {

        String[] lines = tsv.split("\n");
        int linesQuantity = lines.length;

        reportHeader = lines[0];
        Matcher reportHeaderMatcher = REPORT_HEADER_PATTERN.matcher(reportHeader);
        if (!reportHeaderMatcher.matches()) {
            throw new DirectAPIException("Неправильный формат строки отчёта с заголовком и датами");
        }
        reportHeaderName = reportHeaderMatcher.group(1);
        reportHeaderStartDate = reportHeaderMatcher.group(2);
        reportHeaderEndDate = reportHeaderMatcher.group(3);

        columnsHeaders = lines[1];
        columnsHeadersList = Arrays.asList(columnsHeaders.split("\t"));

        totalRows = lines[linesQuantity - 1];

        Matcher totalRowsMatcher = TOTAL_ROWS_PATTERN.matcher(totalRows);
        if (!totalRowsMatcher.matches()) {
            throw new DirectAPIException("Неправильный формат строки отчёта с Total rows");
        }
        totalRowsNumber = Long.valueOf(totalRowsMatcher.group(1));

        reportsLines = new ArrayList<ReportsLine>();
        for (int i = 2; i < linesQuantity - 1; i++) {
            String[] columns = lines[i].split("\t", -1);
            reportsLines.add(getReportsLine(columns, columnsHeadersList));
        }
    }

    public String getColumnsHeaders() {
        return columnsHeaders;
    }

    public void setColumnsHeaders(String columnsHeaders) {
        this.columnsHeaders = columnsHeaders;
    }

    public String getReportHeader() {
        return reportHeader;
    }

    public void setReportHeader(String reportHeader) {
        this.reportHeader = reportHeader;
    }

    public List<ReportsLine> getReportsLines() {
        return reportsLines;
    }

    public void setReportsLines(List<ReportsLine> reportsLines) {
        this.reportsLines = reportsLines;
    }

    public String getTotalRows() {
        return totalRows;
    }

    public void setTotalRows(String totalRows) {
        this.totalRows = totalRows;
    }

    private ReportsLine getReportsLine(String[] columnsData, List<String> fields) {
        ReportsLine reportsLine = new ReportsLine();
        Map<FieldEnum, String> rawFields = new EnumMap<>(FieldEnum.class);
        for (int i = 0; i < columnsData.length; i++) {
            String valueInLine = columnsData[i];
            String fieldWithPostfix = fields.get(i).replaceAll("_\\d+_\\w+", "");
            FieldEnum field = FieldEnum.fromValue(fieldWithPostfix);
            rawFields.put(field, valueInLine);

            switch (field) {
                case AD_GROUP_ID:
                    reportsLine.setAdGroupId(NULL_VALUE.equals(valueInLine) ? null : Long.parseLong(valueInLine));
                    break;
                case AD_GROUP_NAME:
                    reportsLine.setAdGroupName(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case AD_ID:
                    reportsLine.setAdId(NULL_VALUE.equals(valueInLine) ? null : Long.parseLong(valueInLine));
                    break;
                case AD_NETWORK_TYPE:
                    reportsLine.setAdNetworkType(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case AGE:
                    reportsLine.setAge(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case AVG_CPC:
                    reportsLine.setAvgCpc(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case AVG_CPM:
                    reportsLine.setAvgCpm(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case AVG_CLICK_POSITION:
                    reportsLine
                            .setAvgClickPosition(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case AVG_EFFECTIVE_BID:
                    reportsLine.setAvgEffectiveBid(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case COST_PER_CONVERSION:
                    reportsLine
                            .setCostPerConversion(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case AVG_IMPRESSION_FREQUENCY:
                    reportsLine.setAvgImpressionFrequency(
                            NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case AVG_IMPRESSION_POSITION:
                    reportsLine.setAvgImpressionPosition(
                            NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case AVG_PAGEVIEWS:
                    reportsLine.setAvgPageviews(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case AVG_TRAFFIC_VOLUME:
                    reportsLine.setAvgTrafficVolume(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case BOUNCE_RATE:
                    reportsLine.setBounceRate(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case BOUNCES:
                    reportsLine.setBounces(NULL_VALUE.equals(valueInLine) ? null : Integer.parseInt(valueInLine));
                    break;
                case CAMPAIGN_ID:
                    reportsLine.setCampaignId(NULL_VALUE.equals(valueInLine) ? null : Long.parseLong(valueInLine));
                    break;
                case CAMPAIGN_NAME:
                    reportsLine.setCampaignName(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CAMPAIGN_TYPE:
                    reportsLine.setCampaignType(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CAMPAIGN_URL_PATH:
                    reportsLine.setCampaignUrlPath(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CARRIER_TYPE:
                    reportsLine.setCarrierType(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CLICK_TYPE:
                    reportsLine.setClickType(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CLICKS:
                    reportsLine.setClicks(NULL_VALUE.equals(valueInLine) ? null : Integer.parseInt(valueInLine));
                    break;
                case CONVERSIONS:
                    reportsLine.setConversions(NULL_VALUE.equals(valueInLine) ? null : Integer.parseInt(valueInLine));
                    break;
                case CONVERSION_RATE:
                    reportsLine.setConversionRate(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case COST:
                    reportsLine.setCost(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case CRITERIA:
                    reportsLine.setCriteria(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CRITERIA_ID:
                    reportsLine.setCriteriaId(NULL_VALUE.equals(valueInLine) ? null : Long.parseLong(valueInLine));
                    break;
                case CRITERIA_TYPE:
                    reportsLine.setCriteriaType(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CRITERION:
                    reportsLine.setCriterion(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CRITERION_ID:
                    reportsLine.setCriterionId(NULL_VALUE.equals(valueInLine) ? null : Long.parseLong(valueInLine));
                    break;
                case CRITERION_TYPE:
                    reportsLine.setCriterionType(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CTR:
                    reportsLine.setCtr(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case WEIGHTED_CTR:
                    reportsLine.setWeightedCtr(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case WEIGHTED_IMPRESSIONS:
                    reportsLine.setWeightedImpressions(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case DATE:
                    reportsLine.setDate(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case DEVICE:
                    reportsLine.setDevice(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case DYNAMIC_TEXT_AD_TARGET_ID:
                    reportsLine.setDynamicTextAdTargetId(
                            NULL_VALUE.equals(valueInLine) ? null : Long.parseLong(valueInLine));
                    break;
                case EXTERNAL_NETWORK_NAME:
                    reportsLine.setExternalNetworkName(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case GENDER:
                    reportsLine.setGender(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case GOALS_ROI:
                    reportsLine.setGoalsRoi(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case AD_FORMAT:
                    reportsLine.setAdFormat(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case IMPRESSION_REACH:
                    reportsLine
                            .setImpressionReach(NULL_VALUE.equals(valueInLine) ? null : Integer.parseInt(valueInLine));
                    break;
                case IMPRESSION_SHARE:
                    reportsLine.setImpressionShare(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case IMPRESSIONS:
                    reportsLine.setImpressions(NULL_VALUE.equals(valueInLine) ? null : Integer.parseInt(valueInLine));
                    break;
                case KEYWORD:
                    reportsLine.setKeyword(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case LOCATION_OF_PRESENCE_ID:
                    reportsLine.setLocationOfPresenceId(
                            NULL_VALUE.equals(valueInLine) ? null : Integer.parseInt(valueInLine));
                    break;
                case LOCATION_OF_PRESENCE_NAME:
                    reportsLine.setLocationOfPresenceName(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case AUDIENCE_TARGET_ID:
                    reportsLine
                            .setLogicalUserListId(NULL_VALUE.equals(valueInLine) ? null : Long.parseLong(valueInLine));
                    break;
                case MATCH_TYPE:
                    reportsLine.setMatchType(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case MATCHED_KEYWORD:
                    reportsLine.setMatchedKeyword(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case MOBILE_PLATFORM:
                    reportsLine.setMobilePlatform(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case MONTH:
                    reportsLine.setMonth(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case PLACEMENT:
                    reportsLine.setPlacement(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case PROFIT:
                    reportsLine.setProfit(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case QUARTER:
                    reportsLine.setQuarter(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case QUERY:
                    reportsLine.setQuery(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case REVENUE:
                    reportsLine.setRevenue(NULL_VALUE.equals(valueInLine) ? null : new BigDecimal(valueInLine));
                    break;
                case RL_ADJUSTMENT_ID:
                    reportsLine
                            .setRlAdjustmentId(NULL_VALUE.equals(valueInLine) ? null : Integer.parseInt(valueInLine));
                    break;
                case SESSIONS:
                    reportsLine.setSessions(NULL_VALUE.equals(valueInLine) ? null : Integer.parseInt(valueInLine));
                    break;
                case SLOT:
                    reportsLine.setSlot(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case SMART_BANNER_FILTER_ID:
                    reportsLine.setSmartBannerFilterId(
                            NULL_VALUE.equals(valueInLine) ? null : Long.parseLong(valueInLine));
                    break;
                case TARGETING_LOCATION_ID:
                    reportsLine.setTargetingLocationId(
                            NULL_VALUE.equals(valueInLine) ? null : Integer.parseInt(valueInLine));
                    break;
                case TARGETING_LOCATION_NAME:
                    reportsLine.setTargetingLocationName(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case WEEK:
                    reportsLine.setWeek(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case YEAR:
                    reportsLine.setYear(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case TARGETING_CATEGORY:
                    reportsLine.setTargetingCategory(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case INCOME_GRADE:
                    reportsLine.setIncomeGrade(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                case CLIENT_LOGIN:
                    reportsLine.setClientLogin(NULL_VALUE.equals(valueInLine) ? null : valueInLine);
                    break;
                default:
                    throw new DirectAPIException("нет маппинга в ReportsLine для данного поля");
            }
        }
        reportsLine.setRawFields(rawFields);
        return reportsLine;
    }

    public List<String> getColumnsHeadersList() {
        return columnsHeadersList;
    }

    public long getTotalRowsNumber() {
        return totalRowsNumber;
    }

    public String getReportHeaderName() {
        return reportHeaderName;
    }

    public String getReportHeaderStartDate() {
        return reportHeaderStartDate;
    }

    public String getReportHeaderEndDate() {
        return reportHeaderEndDate;
    }
}
