import { IPageColumnDefs } from "./Page";
import { IPageCellStyle, IPageContent, IPageContentCell } from "./Content";
import Utils, { IUtils } from "../Utils";
import Helpers, { IHelpers } from "../Helpers";

export interface IPageSchemaFormatColumn {
  style: IPageCellStyle;
}

export interface IPageSchemaFormat {
  [key: string]: IPageSchemaFormatColumn;
}

export interface IPageSchemaRows {
  header_rows: IPageSchemaFormat[];
  data_rows: IPageSchemaFormat[];
  [key: string]: any;
}

export interface IPageSchema {
  columns: IPageColumnDefs[];
  formats: IPageSchemaRows;
  auto_insert_rows: boolean;
  header_rows: boolean;
  parse(data: any): void;
  toString(): string;
  exportContent(): IPageContent;
  importContent(content: IPageContent, columnKeys: number[], rows?: number, hiddenColumns?: number[]): void;
  [key: string]: any;
}

export class PageSchema implements IPageSchema {
  public columns: IPageColumnDefs[] = [];
  public formats: IPageSchemaRows = {
    header_rows: [],
    data_rows: [],
  };
  public auto_insert_rows: boolean = true;
  public header_rows: boolean = true;

  private utils: IUtils;
  private helpers: IHelpers;

  constructor(data?: any) {
    this.utils = new Utils();
    this.helpers = new Helpers();
    this.parse(data || {});
  }
  public parse(data: any): void {
    ["columns", "formats", "auto_insert_rows", "header_rows"].forEach((key) => {
      if (!data[key]) return;
      if (key === "formats") {
        if (data["formats"].header_rows)
          (<IPageSchema>this)["formats"].header_rows =
            data["formats"].header_rows;
        if (data["formats"].data_rows)
          (<IPageSchema>this)["formats"].data_rows = data["formats"].data_rows;
      } else {
        (<IPageSchema>this)[key] = data[key];
      }
    });
  }
  public get() {
    return {
      columns: this.columns,
      formats: this.formats,
      auto_insert_rows: this.auto_insert_rows,
      header_rows: this.header_rows,
    };
  }
  public toString(): string {
    return JSON.stringify(this.get());
  }
  public importContent(
    content: IPageContent,
    columnKeys: number[] = [],
    rows: number = 2,
    hiddenColumns: number[] = [],
  ): void {
    // if (!columnKeys || !columnKeys.length)
    //   throw new Error("At least one column need to be a primary key");
    if (content.length < 2) throw new Error("At least two rows are required");
    // check if header row contain values
    let hasValue = true;
    let hasPk = false;
    let hasDuplicatePk = false;
    let pkNames: string[] = [];
    let columns: IPageColumnDefs[] = [];
    let headerRow: IPageSchemaFormat = {};
    let dataRow: IPageSchemaFormat = {};
    let dataRows: IPageSchemaFormat[] = [];
    content.forEach((row, rowIndex: number) => {
      if (rowIndex > rows) return;
      dataRow = {};
      row.forEach((cell: any, colIndex: number) => {
        let dataCell = content[1][colIndex];
        let name = this.helpers.safeTitle(`${dataCell.formatted_value}`);
        if (!name) hasValue = false;
        if (!rowIndex) {
          // if (!cell.value) hasValue = false;
          let headerCell = content[0][colIndex];
          let pk = columnKeys.includes(colIndex) || headerCell.pk || false;
          if (pk) hasPk = true;
          let defaultCell = content[2] ? content[2][colIndex] : null;
          if (pkNames.includes(name)) {
            hasDuplicatePk = true;
          }
          pkNames.push(name);
          columns.push({
            pk,
            name: name,
            display_name: `${cell.formatted_value}`,
            default_value: defaultCell ? defaultCell.formatted_value : "",
            hidden: hiddenColumns.includes(colIndex)
          });
          headerRow[name] = {
            style: cell.style,
          };
        }
        // let dataCell = content[1][colIndex];
        dataRow[name] = {
          style: cell.style,
        };
      });
      if (rowIndex) dataRows.push(dataRow);
    });
    if (!hasValue) throw new Error("Header row is missing column names");
    if (!hasPk) throw new Error("No primary keys found");
    if (hasDuplicatePk) throw new Error("Duplicate primary keys found");
    this.columns = columns;
    this.formats.header_rows = [headerRow];
    this.formats.data_rows = dataRows;
  }
  public exportContent(): IPageContent {
    // let content: IPageContent = [];
    if (!this.columns.length) throw new Error("No columns found");
    let headerRow: IPageContentCell[] = [];
    let dataRow1: IPageContentCell[] = [];
    let dataRow2: IPageContentCell[] = [];
    let dataRow3: IPageContentCell[] = [];
    let dataRow4: IPageContentCell[] = [];
    this.columns.forEach((def, colIndex) => {
      let headerFormat = this.getColumnFormat(def.name, true);
      let style = headerFormat.style;
      if (!colIndex) {
        style = { ...this.utils.getDefaultCellStyle(), ...style };
      }
      headerRow.push({
        value: `${def.display_name}`,
        formatted_value: `${def.display_name}`,
        style,
        index: {
          row: 0,
          col: colIndex,
        },
      });
      // field name
      dataRow1.push({
        value: `${def.name}`,
        formatted_value: `${def.name}`,
        style: this.getColumnFormat(def.name, false, 0).style,
        index: {
          row: 1,
          col: colIndex,
        },
      });
      // default column value
      dataRow2.push({
        value: `${def.default_value || ""}`,
        formatted_value: `${def.default_value || ""}`,
        style: {
          ...this.getColumnFormat(def.name, false, 1).style,
        },
        index: {
          row: 2,
          col: colIndex,
        },
      });
      // primary key
      dataRow3.push({
        value: "",
        formatted_value: "",
        style: {
          ...this.utils.getDefaultCellStyle(),
          "text-align": "center",
        },
        index: {
          row: 3,
          col: colIndex,
        },
      });
      // hidden column
      dataRow4.push({
        value: "",
        formatted_value: "",
        style: {
          ...this.utils.getDefaultCellStyle(),
          "text-align": "center",
        },
        index: {
          row: 4,
          col: colIndex,
        },
      });
    });
    return this.utils.mergePageContent([
      headerRow,
      dataRow1,
      dataRow2,
      dataRow3,
      dataRow4,
    ]);
  }
  public getColumnFormat(
    name: string,
    header?: boolean,
    rowIndex: number = 0
  ): IPageSchemaFormatColumn {
    let key = header ? "header_rows" : "data_rows";
    let format: IPageSchemaFormatColumn = {
      style: {},
    };
    if (
      !this.formats[key].length ||
      !this.formats[key][rowIndex] ||
      !this.formats[key][rowIndex][name]
    )
      return format;
    return this.formats[key][rowIndex][name];
  }
}
