import {IField,IActionNode} from "../types/ActionTypes";
import { KintoneRestAPIClient } from "@kintone/rest-api-client";
import { getPageState } from "../util/url";
import $ from 'jquery';
import { Record as KTRecord } from "@kintone/rest-api-client/lib/src/client/types";


// 階層化ドロップダウンメニューの設定インターフェース
export interface ICascadingDropDown {
  sourceApp: IApp;
  dropDownApp: IApp;
  fieldList: IFieldList[];
}

// アプリケーションインターフェース
export interface IApp {
  name: string;
  id: string;
}
// フィールドリストインターフェース
export interface IFieldList {
  source: IField;
  dropDown: IField;
}


// ドロップダウンメニューの辞書タイプ、ドロップダウンオプションの検索を高速化するために使用
type DropdownDictionary = Record<string, string[]>;

// ドロップダウンメニューの処理クラス
export class DropDownManager {
  private dictionary: DropdownDictionary = {};
  private state: Record<string, string> = {};
  private selects: Map<string, HTMLElement> = new Map();
  private columnMap: Map<HTMLElement, IFieldList> = new Map();
  private columMapValueArray: IFieldList[] = [];
  private columMapArray: [HTMLElement, IFieldList][] = [];
  private props :ICascadingDropDown;
  private event:Event;

  constructor(props: ICascadingDropDown,event:any){
    this.props=props;
    this.event=event;
  }

  // 初期化メソッド
  async init(): Promise<void> {
    try {
      const client = new KintoneRestAPIClient();
      const sourceAppId = this.props.sourceApp.id;
      const fields = this.props.fieldList.map((f) => f.source.code);
      const records = await client.record.getAllRecords({
        app: sourceAppId,
        fields,
      });
      this.dictionary =this.buildDropdownDictionary(records, this.props.fieldList);
      // const hash = await this.calculateHash(records, this.actionNode);
      // const storageKey = `dropdown_dictionary::${this.props.dropDownApp.id}_${hash}`;

      // const lsDictionary = this.getFromLocalStorage(storageKey);
      // this.dictionary =
      //   lsDictionary || this.buildDropdownDictionary(records, this.props.fieldList);
      // if (!lsDictionary) {
      //   this.saveToLocalStorage(storageKey, this.dictionary);
      // }
    } catch (error) {
      console.error(
        "階層化ドロップダウンの初期化中にエラーが発生しました:",
        error
      );
      throw error;
    }
  }

  // Web Crypto APIを使用してハッシュ値を計算
  private async calculateHash(
    records: KTRecord[],
    actionNode: IActionNode
  ): Promise<string> {
    const str = JSON.stringify(records) + JSON.stringify(actionNode);
    const encoder = new TextEncoder();
    const data = encoder.encode(str);
    const hashBuffer = await crypto.subtle.digest("SHA-1", data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray
      .map((b) => b.toString(16).padStart(2, "0"))
      .join("");
    return hashHex;
  }

  /**
   * ドロップダウンメニューの辞書を構築
   * @param records データソースのレコード
   * @param fieldList データソースの階層フィールド
   * @returns 
   */
  private buildDropdownDictionary(records: KTRecord[],fieldList: IFieldList[]): DropdownDictionary {
    const tempDictionary: Record<string, Set<string>> = { "0_TOP": new Set() };
    const fieldCodeToIndexMap = new Map(
      fieldList.map((field, index) => [field.source.code, index])
    );

    records
      .map((record) =>
        Object.entries(record)
          .map(([fieldCode, fieldData]) => ({
            fieldCode,
            value: fieldData.value,
            index: fieldCodeToIndexMap.get(fieldCode),
          }))
          .filter((item) => item.index !== undefined)
          .sort((a, b) => a.index! - b.index!)
      )
      .forEach((recordArray) => {
        recordArray.forEach((item, index, array) => {
          const { value, fieldCode } = item;
          if (!value) return;
          const v = value as string;

          if (index === 0) {
            tempDictionary["0_TOP"].add(v);
          } else {
            const previousItem = array[index - 1];
            const previousKey = `${previousItem.index}_${previousItem.value}`;
            tempDictionary[previousKey] =
              tempDictionary[previousKey] || new Set();
            tempDictionary[previousKey].add(v);
          }
        });
      });

    const dictionary: DropdownDictionary = {};
    for (const [key, set] of Object.entries(tempDictionary)) {
      dictionary[key] = Array.from(set).sort();
    }

    return dictionary;
  }

  // ローカルストレージから辞書を取得
  private getFromLocalStorage(key: string): DropdownDictionary | null {
    const data = localStorage.getItem(key);
    this.clearUpDictionary(key);
    return data ? JSON.parse(data) : null;
  }

  // 古い辞書をクリア
  private clearUpDictionary(key: string): void {
    Object.keys(localStorage)
      .filter((k) => k.startsWith("dropdown_dictionary::") && !k.endsWith(key))
      .forEach((k) => localStorage.removeItem(k));
  }

  // ローカルストレージに辞書を保存
  private saveToLocalStorage(key: string, data: DropdownDictionary): void {
    localStorage.setItem(key, JSON.stringify(data));
  }

  // ページの状態を処理
  async handlePageState(appId: string): Promise<void> {
    const currentState = getPageState(
      window.location.href.replace("#", "?"),
      appId
    );

    switch (currentState.type) {
      case "app":
      case "edit":
      case "show":
        if (currentState.type === "show" && currentState.mode !== "edit") break;
        await this.init();
        this.setupCascadingDropDown(currentState.type);
        break;
    }
  }

  // 階層化ドロップダウンを設定する
  setupCascadingDropDown(pageType: string): void {
    const tableElement = document.getElementById(
      pageType === "app" ? "view-list-data-gaia" : "record-gaia"
    );
    if (!tableElement) {
      console.error("ルート要素が見つかりません");
      return;
    }
    if(pageType==="app"){
      this.initDropdownContainerForList();
    }else{
      this.initDropdownContainerforDetail(tableElement);
    }
    this.renderDropdownContainer();
  }


  /**
   * ドロップダウンメニューコンテナを初期化(一覧編集画面)
   * @param tableElement 
   */
  private initDropdownContainerForList(){
    const fieldLists = this.props.fieldList;
    fieldLists.forEach((fld,index,array)=>{
       const elems = kintone.app.getFieldElements(fld.dropDown.code);
       if(elems){
        const editElem = $(elems).filter(".recordlist-editcell-gaia").get(0);
        if(editElem!==undefined){
          this.columnMap.set(editElem, fld);
        }
       }
    });
    this.columMapArray = Array.from(this.columnMap.entries());
    this.columMapValueArray = Array.from(this.columnMap.values());
  }

  // ドロップダウンメニューコンテナを初期化(明細編集画面)
  private initDropdownContainerforDetail(tableElement: HTMLElement): void {
    const fieldList = this.props.fieldList;
    let headerCells = $(tableElement).find(".control-gaia");
    for (const field of fieldList) {
      const cell = headerCells.has(`div.control-label-gaia span:contains("${field.dropDown.label}")`).get(0);
      if(cell!==undefined){
        const valueElem = $(cell).find(".control-value-gaia").get(0);
        if(valueElem!==undefined){
          this.columnMap.set(cell, field);
        }
      }
    }
    this.columMapArray = Array.from(this.columnMap.entries());
    this.columMapValueArray = Array.from(this.columnMap.values());
  }

  // ドロップダウンメニューをレンダリング
  private renderDropdownContainer(): void {
    this.columnMap.forEach((field, cell) => {
      // const cell = cells[columnIndex];
      if (!cell) return;

      const input = cell.querySelector<HTMLInputElement>("input");
      if (!input) return;

      this.createSelect(input, field.dropDown.code);
    });
  }

  // ドロップダウンメニューを作成
  private createSelect(input: HTMLInputElement, fieldCode: string): void {
    const div = document.createElement("div");
    div.className = "bs-scope";
    div.style.margin = "0.12rem";
    const select = document.createElement("select");
    select.className = "custom-dropdown form-select";
    select.dataset.field = fieldCode;
    select.addEventListener("change", (event) =>{
      this.selectorChangeHandle(fieldCode, event);
      input.value=this.state[fieldCode];
    });
    div.appendChild(select);
    this.updateOptions(fieldCode, select, input.value);
    input.parentNode?.insertBefore(div, input.nextSibling);
    input.style.display = "none";
    this.selects.set(fieldCode, div);

  }

  // ドロップダウンメニューのオプションを更新
  private updateOptions(
    fieldCode: string,
    initSelect?: HTMLSelectElement | undefined | null,
    value?: string
  ): void {
    let select = initSelect;
    if (!initSelect) {
      select = this.selects
        .get(fieldCode)
        ?.querySelector<HTMLSelectElement>("select");
      if (!select) {
        console.error(
          `フィールド ${fieldCode} のドロップダウンメニュー要素が見つかりません`
        );
        return;
      }
    } else {
      this.state[fieldCode] = value!;
    }

    const field = this.props.fieldList.find((f) => f.dropDown.code === fieldCode);
    if (!field) {
      console.error(`フィールド ${fieldCode} の設定が見つかりません`);
      throw new Error(`フィールド ${fieldCode} の設定が見つかりません`);
    }

    const level = this.getLevel(fieldCode);
    const previousValue = this.getPreviousValueFromState(fieldCode);
    const options = this.getOptions(level, previousValue);
    if (!select) {
      return;
    }

    select.innerHTML = '<option value="">選択してください</option>';
    options.forEach((option) => {
      const optionElement = document.createElement("option");
      optionElement.value = option;
      optionElement.textContent = option;
      select?.appendChild(optionElement);
    });

    select.value = value ?? this.state[fieldCode] ?? "";
    select.disabled = level > 0 && !previousValue;
  }

  // ドロップダウンメニューが変更された後にトリガーされる関数
  private selectorChangeHandle=(fieldCode: string, event: Event)=> {
    const select = event.target as HTMLSelectElement;
    this.state[fieldCode] = select.value;
    const currentLevel = this.getLevel(fieldCode);
    this.columMapArray
      .filter((_, arrayIndex) => arrayIndex > currentLevel)
      .forEach(([_, field]) => {
        const fieldCode = field.dropDown.code;
        this.updateOptions(fieldCode);
        const inputElem = this.selects.get(fieldCode)?.previousElementSibling as HTMLInputElement;
        if(inputElem){
          inputElem.value="";
        }
        delete this.state[fieldCode];
      });
  }

  // フィールドのレベルを取得
  private getLevel(fieldCode: string): number {
    return this.columMapValueArray.findIndex(
      (field) => field.dropDown.code === fieldCode
    );
  }

  // 前のレベルのフィールドの値を取得
  private getPreviousValueFromState(fieldCode: string): string {
    const currentIndex = this.getLevel(fieldCode);
    if (currentIndex <= 0) return "";
    const previousField = this.columMapValueArray[currentIndex - 1];
    return this.state[previousField.dropDown.code] ?? "";
  }

  // 指定された階層と値のドロップダウンメニューオプションを取得
  private getOptions(level: number, value: string): string[] {
    const key = level === 0 ? "0_TOP" : this.buildKey(level - 1, value);
    return this.dictionary[key] || [];
  }

  // ドロップダウンメニューのキーを構築
  private buildKey(level: number, value: string): string {
    return `${level}_${value}`;
  }
}