<template>
  <operation-dialog
    v-model="opened"
    v-bind="$attrs"
    v-on="$listeners"
    @hide="clear"
  >
    <q-form ref="form" class="row q-col-gutter-md" greedy @submit="submit">
      <div class="col-12">
        <data-block
          :form-data="{ label: 'Наименование отчета' }"
          bolder
          no-padding
        >
          <select-base
            :options="[baseReport]"
            :value="baseReport"
            option-label="name"
            option-value="id"
            disable
            :title="baseReport && baseReport.name"
          />
        </data-block>
      </div>
      <div v-for="(option, i) in options" :key="i" class="col-12">
        <data-block :form-data="{ label: $t(option.label) }" bolder no-padding>
          <component
            v-bind="mapLinked(option.props, option.linked)"
            :is="option.component"
            v-model="result[option.name]"
            :style="option.style"
            v-on="option.listeners"
            @isAllSelected="(e) => setAllSelected(e, option.name)"
            @proxyOrganisations="addProxyOrganisation"
            @department="addProxyDepartment"
            :role="reportAccessRole"
            :isSubDepartment="baseReport && baseReport.isSubDepartment"
          />
        </data-block>
      </div>
    </q-form>
    <template #right-actions>
      <r-btn
        icon="pgs-check"
        label="Создать отчет"
        color="primary"
        :disabled="emptyFieldChecker"
        action
        @click="$refs.form.submit()"
      />
    </template>
  </operation-dialog>
</template>
<script lang="ts">
import {
  Component,
  Mixins,
  ModelSync,
  Prop,
  Vue,
} from 'vue-property-decorator';
import { oidcClientComputed } from '@state/modules/oidc-client';
import { set, map, get, isEmpty, merge, isNull } from 'lodash';
import { convertDateFormat, doMathWithDate } from '@utils/format-date';
import { DateFormat } from '@constants/utils/format-date';
import { getNameStrings, mapOptionsResults } from '@components/reports/utils';
import dataBlock from '@components/cards/statements/components/dataBlock.vue';
import SelectAccounts from '../components/select-accounts.vue';
import SelectRegions from '../components/select-regions.vue';
import SelectOrganizations from '../components/select-organizations.vue';
import SelectBase from '../components/select-base.vue';
import XDatetimeRange from '../components/x-datetime-range.vue';
import SelectEvery from '../components/select-every.vue';
import { USER_SCOPES } from '@constants/entities/Scope';
import { Region, RegionApi, DepartmentApi, DomainApi } from '@openapi/idm';
import {
  OrganizationApi,
  Organization,
  Department,
} from '@openapi/registry-service';
import findQueryDefinitionId, {
  BaseReport,
  BaseReportCreateColumn,
  Report,
} from '../../model';
import { reportScheduler as reportSchedulerApi } from '@/recruits/api/stat-report-scheduler';
import { notifyError } from '@utils/notify';
import { getAuthorizationType, AUTH_LOCAL } from '@/utils/save-profile-info';
import SummonDate from '@/recruits/mixins/summon-date';
import XDateTimeRange from '@/recruits/components/reports/list/components/x-date-time-range.vue';
import XCheckbox from '@/recruits/components/reports/list/components/x-checkbox/index.vue';
const departmentApi = new DepartmentApi();
const organizationApi = new OrganizationApi();
const domainApi = new DomainApi();

const reports = Vue.extend({
  computed: {
    ...oidcClientComputed,
  },
});

interface DepartmentWithParent extends Department {
  parent: Organization;
}

type ReportParams = {
  regions?: Record<string, string>[];
  organizations?: Record<string, string>[];
  services?: Record<string, string>[];
};

export enum reportAccessScope {
  MILITARY_COMMISSAR = 'military_commissar',
  GOMU_SPECIALIST = 'gomu_specialist',
  SYSTEM_ADMINISTRATOR = 'system_administrator',
}

const startOfDay = new Date(new Date().setHours(0,0,0,0));
const endOfDay = new Date(new Date().setHours(23,59,59,999));

function createYear17Array(limit: number): string[] {
  const arr = [];
  const start = new Date().getFullYear() - 1;
  for (let i = 0; i < limit; i++) {
    arr.push((i + start).toString());
  }
  return arr;
}

const sortTypeOptions = [
  {
    label: 'По дате применения меры',
    value: 'access',
  },
  {
    label: 'По дате отмены меры',
    value: 'decline',
  },
];

@Component({
  components: {
    dataBlock,
    SelectAccounts,
    SelectRegions,
    SelectOrganizations,
    SelectBase,
    SelectEvery,
    XDatetimeRange,
    XDateTimeRange,
    XCheckbox,
  },
})
export default class CreateReportDialog extends Mixins(reports, SummonDate) {
  @ModelSync('value', 'change')
  public opened: boolean;

  @Prop() protected baseReport!: BaseReport;
  @Prop() protected defaultParams: ReportParams;

  protected reportAccessRole: reportAccessScope | null = null;
  protected currentUserRegion: Region | null = null;
  protected currentUserOrganization: Organization | null = null;
  USER_SCOPES = USER_SCOPES;

  public submit(): void {
    this.save();
    this.opened = false;
  }

  protected get notAuto() {
    return !this.auto;
  }

  protected mapLinked(
    props: BaseReportCreateColumn['props'] = {},
    linked?: BaseReportCreateColumn['linked']
  ) {
    if (!linked || !this.visibleColums.length) {
      return props;
    }
    // fixme
    map(linked, ({ column, default: def, value }, key) => {
      if (this.visibleColums.includes(column)) {
        if (value === undefined) return;
        if (value.startsWith('!!')) {
          value = value.slice(2);
          props[key] = !!get(this, value);
        } else if (value.startsWith('!')) {
          value = value.slice(1);
          props[key] = !get(this, value);
        } else {
          props[key] = get(this, value);
        }
      } else {
        props[key] = def;
      }
    });

    return props;
  }

  get emptyFieldChecker() {
    if (this.result === void 0) {
      return true;
    }
    return Object.keys(this.result)?.reduce((acc, e) => {
        if (isNull(this.result?.[e]) && e !== 'regions') {
          acc = true;
        }
        // else if (typeof this.result?.[e] === 'object' && Object.keys(this.result?.[e])?.length) {
        //   Object.keys(this.result?.[e]).reduce((nested, n) => {
        //     if (isNull(this.result[e]?.[n])) {
        //       acc = true;
        //     }
        //     return nested;
        //   }, false);
        // }
        return acc;
      }, false);
  }

  protected columns: Array<BaseReportCreateColumn> = [
    {
      field: 'params.regions',
      label: 'reports.columns.region',
      name: 'regions',
      component: 'select-regions',
      props: {
        multiple: true,
        required: true,
      },
      fieldMap: ['id', 'code', 'name'],
    },
    {
      field: 'params.organizations',
      label: 'reports.columns.organization',
      name: 'organizations',
      component: 'select-organizations',
      props: {
        multiple: true,
      },
      linked: {
        regions: {
          column: 'regions',
          default: 'all',
          value: 'result.regions',
        },
        required: {
          column: 'regions',
          default: true,
        },
      },
      fieldMap: ['id', 'fullname', 'shortname'],
    },
    // {
    //   field: 'params.services',
    //   label: 'reports.columns.serviceName',
    //   name: 'services',
    //   component: 'select-services',
    //   props: {
    //     multiple: true,
    //   },
    //   linked: {
    //     organizations: {
    //       column: 'organizations',
    //       default: 'all',
    //       value: 'result.organizations',
    //     },
    //     required: {
    //       column: 'organizations',
    //       default: true,
    //     },
    //   },
    //   fieldMap: ['id', 'targetId', 'fullName', 'shortName', 'shortNameStat'],
    // },
    {
      field: 'params.accounts',
      label: 'reports.columns.accounts',
      name: 'accounts',
      component: 'select-accounts',
      props: {
        multiple: true,
      },
      linked: {
        organizations: {
          column: 'organizations',
          default: 'all',
          value: 'result.organizations',
        },
      },
      fieldMap: {
        id: 'id',
        fullname: 'fullname',
        shortname: 'shortname',
        firstname: 'person.firstname',
        middlename: 'person.middlename',
        surname: 'person.surname',
      },
    },
    {
      field: 'comment',
      label: 'reports.columns.comment',
      name: 'comment',
      component: 'q-input',
      props: {
        dense: true,
        filled: true,
      },
    },
    {
      field: 'params.year',
      label: 'Выбор периода',
      name: 'year',
      component: 'q-select',
      props: {
        options: createYear17Array(10),
        dense: true,
        filled: true,
      },
    },
    {
      field: 'params.sortBy',
      label: 'Условия выборки данных',
      name: 'sortBy',
      component: 'q-select',
      props: {
        options: sortTypeOptions,
        dense: true,
        filled: true,
      },
    },
    {
      field: 'params.range',
      label: '',
      name: 'range',
      component: 'x-datetime-range',
      props: {
        dense: true,
        filled: true,
        rangeLabel: {
          from: 'с',
          to: 'по',
        },
        default: {
          to: convertDateFormat(new Date(), '', DateFormat.backend),
          from: convertDateFormat(
            doMathWithDate('subMonths', new Date(), 1),
            '',
            DateFormat.backend
          ),
        },
      },
      linked: {
        disable: {
          column: 'every',
          default: false,
          value: 'auto',
        },
        required: {
          column: 'every',
          default: true,
          value: 'notAuto',
        },
      },
      style: 'display: flex; flex-wrap: nowrap',
    },
    {
      field: 'params.range',
      label: '',
      name: 'rangeWithTime',
      component: 'x-date-time-range',
      props: {
        dense: true,
        filled: true,
        format: DateFormat.backend,
        rangeLabel: {
          from: 'с',
          to: 'по',
        },
        default: {
          from: convertDateFormat(startOfDay, '', DateFormat.datetime),
          to: convertDateFormat(endOfDay, '', DateFormat.datetime),
        },
      },
      linked: {
        required: {
          column: 'rangeWithTime',
          default: true,
          value: 'notAuto',
        },
      },
    },
    {
      field: 'params.reportEnabled',
      label: '',
      name: 'test-organisation',
      component: 'x-checkbox',
      props: {
        label: 'с учетом тестовых',
        default: false,
      },
      linked: {
        required: {
          column: 'reportEnabled',
          default: true,
          value: 'notAuto',
        },
      },
    },
    {
      auto: true,
      field: 'auto.startDate',
      label: 'reports.columns.startDateFirst',
      name: 'startDate',
      component: 'x-date-input',
      props: {
        dense: true,
        filled: true,
      },
      style: 'width: 100%',
    },
    {
      auto: true,
      field: 'auto.last',
      label: 'reports.columns.last',
      name: 'last',
      props: {
        emitArray: false,
        required: true,
      },
      component: 'select-every',
    },
    {
      auto: true,
      field: 'auto.every',
      label: 'reports.columns.last', // 'reports.columns.everu'
      name: 'every',
      props: {
        emitArray: false,
        required: true,
      },
      component: 'select-every',
    },
  ];

  protected get mappedColumns(): BaseReportCreateColumn<string>[] {
    let cols: any = [];
    const baseCreate = this.baseReport?.columns?.create;
    // const observerRFCreate = this.baseReport?.columns?.observerRFCreate;
    // const observerRegionCreate = this.baseReport?.columns?.observerRegionCreate;
    // const observerOrganizationCreate = this.baseReport?.columns
    //   ?.observerOrganizationCreate;
    switch (this.reportAccessRole) {
      // case reportAccessScope.MILITARY_COMMISSAR:
      //   cols = this.reduceColumn(observerRFCreate || baseCreate);
      //   break;
      // case reportAccessScope.GOMU_SPECIALIST:
      //   cols = this.reduceColumn(observerRegionCreate || baseCreate);
      //   break;
      // case reportAccessScope.SYSTEM_ADMINISTRATOR:
      //   cols = this.reduceColumn(observerOrganizationCreate || baseCreate);
      //   break;
      default:
        cols = this.reduceColumn(baseCreate);
        break;
    }

    if (isEmpty(cols)) {
      return this.columns;
    } else {
      return this.columns.map(
        (col) =>
          cols && (col.name in cols ? merge({}, col, cols[col.name]) : col)
      );
    }
  }

  protected reduceColumn(
    cols: Array<string | BaseReportCreateColumn<string>> | undefined
  ): BaseReportCreateColumn[] {
    if (!cols) return [];
    return cols
      .filter(
        (col: string | BaseReportCreateColumn): col is BaseReportCreateColumn =>
          typeof col !== 'string'
      )
      .reduce((o, v) => {
        o[v.name] = v;
        return o;
      }, Object.create(null));
  }

  protected result: any = {};
  protected resultAll: { [key: string]: boolean } = {};
  protected auto = false;
  protected proxyOrganisation: unknown[] = [];
  protected proxyDepartment: unknown[] = [];

  protected get visibleColums() {
    return (
      this.baseReport?.columns?.create?.map((col) =>
        typeof col === 'string' ? col : col.name
      ) || []
    );
  }

  protected get options(): BaseReportCreateColumn<string>[] {
    return this.mappedColumns.filter(({ name, auto, props }) => {
      return (
        !auto &&
        !props?.hidden &&
        (props?.required || this.visibleColums.includes(name))
      );
    });
  }

  protected get optionsWithHidden(): BaseReportCreateColumn<string>[] {
    return this.mappedColumns.filter(({ name, auto, props }) => {
      return !auto && (props?.required || this.visibleColums.includes(name));
    });
  }

  protected get autoOptions(): BaseReportCreateColumn<string>[] {
    return this.mappedColumns.filter(({ name, props, auto }) => {
      return auto && (props?.required || this.visibleColums.includes(name));
    });
  }

  protected async created() {
    this.clear();
    this.setRole();
    if (!isEmpty(this.defaultParams)) {
      this.setDefault(this.defaultParams);
    }
  }

  protected clear() {
    this.result = this.options.reduce((o, { name }) => {
      o[name] = null;
      return o;
    }, Object.create(null));
    if (this.currentUserRegion) {
      this.result.regions = [this.currentUserRegion];
    }
    if (this.currentUserOrganization) {
      this.result.organizations = [this.currentUserOrganization];
    }
  }

  protected async setRole(): Promise<void> {
    if (this.$AC.haveOneOfScope([USER_SCOPES.GOMU_SPECIALIST])) {
      this.reportAccessRole = reportAccessScope.GOMU_SPECIALIST;
      return;
    }
    if (this.$AC.haveOneOfScope([USER_SCOPES.MILITARY_COMMISSAR])) {
      this.reportAccessRole = reportAccessScope.MILITARY_COMMISSAR;
      await this.setDefaultRegion();
      this.result.regions = [this.currentUserRegion];
      this.result.organizations = [this.currentUserOrganization];
      return;
    }
    if (
      this.$AC.haveOneOfScope([
        USER_SCOPES.MILITARY_COMMISSARIAT_ADMINISTRATOR,
        USER_SCOPES.MILITARY_RECRUIT_SPECIALIST,
        // USER_SCOPES.MILITARY_CONSCRIPTION_SPECIALIST,
      ])
    ) {
      this.reportAccessRole = reportAccessScope.MILITARY_COMMISSAR;
      await this.setDefaultRegion();
      this.result.regions = [this.currentUserRegion];
      this.result.organizations = [this.currentUserOrganization];
    }
  }

  protected async setDefaultRegion() {
    const { currentUser } = this;
    let currentUserOrganization = null;
    let currentUserRegionId;
    let currentUserDepartment = null;
    let parentOrganization = null;
    const authType = getAuthorizationType(currentUser);
    if (authType === AUTH_LOCAL || !!currentUser.ogrn) {
      try {
        currentUserOrganization = await organizationApi
          .getOrganization(currentUser.domain_id)
          .then(({ data }) => data);
        currentUserRegionId = currentUserOrganization?.regionId;
      } catch (e) {
        currentUserDepartment = await departmentApi
          .getDepartment(currentUser.domain_id, 'parent')
          .then(({ data }) => data);

        parentOrganization = await organizationApi
          .getOrganization(
            (currentUserDepartment as DepartmentWithParent)?.parent.id
          )
          .then(({ data }) => data);
        currentUserRegionId = parentOrganization.regionId;
      }
    } else {
      currentUserOrganization = null;
      currentUserRegionId = currentUser.domain_id;
    }

    const currentUserRegion: Region = await new RegionApi()
      .getRegion(currentUserRegionId)
      .then(({ data }) => data);

    if (currentUserRegion) {
      this.currentUserRegion = currentUserRegion;
    }
    if (currentUserOrganization) {
      this.currentUserOrganization = currentUserOrganization;
    } else if (parentOrganization) {
      this.currentUserOrganization = parentOrganization;
    }
  }

  protected setDefault(params: ReportParams): void {
    if (params.regions?.length) {
      // this.result.regions = params.regions;
    }
    if (params.organizations?.length) {
      this.result.organizations = params.organizations;
    }
    if (params.services?.length) {
      const servicesWithShortNameStat = params.services.filter(
        (service) => service.shortNameStat
      );
      this.result.services = servicesWithShortNameStat;
    }
  }

  async compareAccessForReport(report: Report) {
    if(report.queryDefinitionId) {
      if (
        this.$AC.haveOneOfScope([
          USER_SCOPES.GOMU_SPECIALIST,
          USER_SCOPES.GOMU_OBSERVER,
          USER_SCOPES.SYSTEM_ADMINISTRATOR,
        ])) {
        report.params.allVks = true;
      } else if (this.$AC.haveOneOfScope([
          USER_SCOPES.MILITARY_COMMISSAR,
          USER_SCOPES.MILITARY_RECRUIT_SPECIALIST,
        ])) {
        report.params.allVks = false;
        const domainId = this.$store.getters['oidcClient/currentUser'].domain_id;
        const data: any = await domainApi.getDomainChildrenTreeV1(domainId);
        if (data.data.leafs.length) {
          report.params.vks = this.getDepartmentId(data.data.leafs);
        } else {
          report.params.vks = [{ id: data.data?.value?.id , name: data.data?.value?.name }]
        }
      }
    }
  };


  getDepartmentId(vkArray: any[]) {
    return vkArray.reduce((acc: any[], vk: any) => {
      if (vk.value.schema === 'Department') {
        acc.push({
          id: vk.value.id,
          name: vk.value.name,
        });
      }
      return acc;
    }, []);
  }

  protected async save() {
    let { optionsWithHidden, currentUser, auto } = this;
    const report: Report = Object.create(null);

    report.parentId = this.baseReport.id;
    report.name = this.baseReport.name;
    if (auto) {
      report.auto = {
        enabled: true,
      };
      optionsWithHidden = optionsWithHidden.concat(this.autoOptions);
    }

    if (currentUser) {
      // eslint-disable-next-line camelcase,@typescript-eslint/naming-convention
      const {
        given_name,
        middle_name,
        family_name,
        sub,
        domain_id,
      } = currentUser;
      report.creator = {
        id: sub,
        organizationId: domain_id,
        firstname: given_name,
        middlename: middle_name,
        surname: family_name,
        ...getNameStrings(given_name, middle_name, family_name),
      };
    }
    Object.assign(report, mapOptionsResults(optionsWithHidden, this.result));
    Object.assign(report.params, this.resultAll);
    if (report.parentId === '11') {
      if (report.params.allRegions) {
        report.params.regions = [];
      }
      if (report.params.allOrganizations) {
        report.params.organizations = [];
      }
      // TODO: Нужно разобраться почему задуманная логика не работает с отчётом 11 (во всяком случае с услугами)
      // if (report.params.allServices) {
      //   report.params.services = [];
      // }
    }

    if (report.auto) {
      report.auto.enabled = true;
      delete report.params.range;
    }

    const queryDefinitionId = findQueryDefinitionId(
      this.baseReport,
      report,
      this.reportAccessRole || undefined
    );

    if (!queryDefinitionId) {
      notifyError('queryDefinitionId не найден');
      return;
    }

    report.queryDefinitionId = queryDefinitionId;

    // проверяем права сотрудника по отчетам
    await this.compareAccessForReport(report);

    report.number = +convertDateFormat(new Date(), '', DateFormat.dateNumber);

    report.source = this.baseReport.source;

    try {
      // const save = documentApi
      //   .createDocument('StatReport', [report as any]) // TODO: ERROR! Type 'Report' is not assignable to type 'Document'.
      //   .then(({ data: response }) =>
      //     set(report, 'id', get(response, 'data.objectIds[0]'))
      //   );

      // TODO: пока не резбирался как правильно настроить что бы отчёт был без этих параметров
      // Рома просил убрать, но когда они понадобатся то возможно это место будет кому-то мозги трепать
      delete report.params.organizations;
      delete report.params.regions;

      const save = reportSchedulerApi
        .create(report)
        .then(({ data: response }: { data: any }) =>
          set(report, 'id', get(response, 'data.objectIds[0]'))
        );

      this.$emit('creating', save);

      this.$recordActivity({
        buttonCode: 'report_create',
        extraInfo: {
          previewInfo: [
            {
              label: 'Наименование отчета',
              value: this.baseReport.name,
            },
          ],
        },
      });
      this.clear();
    } catch (e) {
      notifyError(e.message.toString());
    }
  }

  setAllSelected(value: boolean, name: string) {
    const capitalizedName = name.slice(0, 1).toUpperCase() + name.slice(1);
    this.resultAll[`all${capitalizedName}`] = !!value;
  }

  addProxyOrganisation(org: Organization[]) {
    this.proxyOrganisation = org;
  }

  addProxyDepartment(org: Organization[]) {
    this.proxyDepartment = org;
  }
}
</script>
