import { Inject, Injectable, Optional } from '@angular/core';
import { FancyDragDrop } from './fancy-selection.model';
import { ISeqBusinessEntity } from '../../models/base.model';
import { moveItemInArray } from '@angular/cdk/drag-drop';
import { UpdateSeqViewModel } from '../../shared/models/common.model';
import { NumberUtils } from '../../utilities/number-utils';

export const SEQ_INTERVAL = 'SEQ_INTERVAL_DEFAULT';
export const DEFAULT_SEQ_INTERVAL = 1000;
@Injectable({
  providedIn: 'root'
})
export class SeqCalcService {
  private SEQ_INTERVAL_REGULAR_AMOUNT = 10; // Regular interval amount for seq calculation
  private baseInterval: number;
  constructor(
    @Optional() @Inject(SEQ_INTERVAL) baseInterval: number | null,
  ) {

    // use DEFAULT when token missing
    this.baseInterval = baseInterval ?? DEFAULT_SEQ_INTERVAL;

  }

  calculateAndAssignSeq(event: FancyDragDrop<ISeqBusinessEntity>,
    allData: ISeqBusinessEntity[], updateSeqFunc: (model: UpdateSeqViewModel) => void) {
    let movedRows = [event.focusRow.rowDatum] as ISeqBusinessEntity[];
    this.calculateAndAssignSeqs(event, allData, movedRows, updateSeqFunc);
  }
  calculateAndAssignSeqs(event: FancyDragDrop<ISeqBusinessEntity>,
    allData: ISeqBusinessEntity[],
    movedRows: ISeqBusinessEntity[], updateSeqFunc: (model: UpdateSeqViewModel) => void) {
    let intervalCount = this.baseInterval;
    let insertSeq = 0;
    //allData = allData.sort((a, b) => a.seq - b.seq); // Ensure allData is sorted by seq before processing
    //Find the start seq, and interval amount for moving
    if (event.previousRow && event.nextRow) {
      console.log('previousRowSeq', event.previousRow.rowDatum.seq, event.nextRow.rowDatum.seq);
      if (movedRows.length > 1) {
        insertSeq = (event.previousRow.rowDatum.seq + event.nextRow.rowDatum.seq) / (movedRows.length + 1);
        intervalCount = insertSeq + (insertSeq % 2);
      } else {
        insertSeq = (event.previousRow.rowDatum.seq + event.nextRow.rowDatum.seq) / 2;
      }
    } else if (event.previousRow) {
      insertSeq = event.previousRow.rowDatum.seq + this.baseInterval;
    }
    else if (event.nextRow) {
      intervalCount = event.nextRow.rowDatum.seq / movedRows.length;
      intervalCount += intervalCount % 2;
    }
    let updateDocuments = [] as ISeqBusinessEntity[];
    // if (this.tableSettings.taggedRowData.length > 0) {
    //   movedRows = this.tableSettings.taggedRowData as DocumentGroupTemplate[];
    // } 

    //moveItemInArray(allData, event.previousIndex, event.currentIndex);
    let needToUpdateAllRows = false;
    let lastSeq = null;
    for (let i = 0; i < movedRows.length; i++) {
      const row = movedRows[i];
      row.seq = Math.round(insertSeq + intervalCount * i);
      if (lastSeq != null && (row.seq - lastSeq) === 0) {
        needToUpdateAllRows = true;
        break;
      }
      lastSeq = row.seq;
      allData.find(d => d.id == row.id).seq = row.seq;
      if (allData.filter(d => d.seq == row.seq).length > 1) {
        needToUpdateAllRows = true;
      }
      updateDocuments.push(row);
      console.log('newSeq', row.seq);
    }

    allData = allData.sort((a, b) => a.seq - b.seq); // Ensure allData is sorted by seq after processing

    if (needToUpdateAllRows) {
      this.resetAllSeq(allData, updateSeqFunc, allData[0].seq);
    } else {

      updateSeqFunc(
        { ids: updateDocuments.map(d => d.id), seqs: updateDocuments.map(d => d.seq) } as UpdateSeqViewModel
      );
    }

  }

  resetAllSeq(allData: ISeqBusinessEntity[], updateSeqFunc: (model: UpdateSeqViewModel) => void, startSeq: number = 0) {
    let seq = startSeq;
    for (let i = 0; i < allData.length; i++) {
      const document = allData[i];
      document.seq = seq;
      seq += this.baseInterval;
    }
    if (updateSeqFunc) {
      updateSeqFunc(
        { ids: allData.map(d => d.id), seqs: allData.map(d => d.seq) } as UpdateSeqViewModel
      );
    }
  }

  calculateNewSeq(
    allData: ISeqBusinessEntity[],
    focusedDatum: ISeqBusinessEntity,
    insertAfter: boolean,
    updateSeqFunc: (model: UpdateSeqViewModel) => void
  ): number {
    if (!Array.isArray(allData) || !focusedDatum) {
      throw new Error("Invalid arguments.");
    }

    const base = this.baseInterval;
    const idx = allData.indexOf(focusedDatum);
    if (idx === -1) throw new Error("focusedDatum is not in allData.");


    // Helper: resequence using the current array order
    const resequence = () => {
      for (let i = 0; i < allData.length; i++) {
        allData[i].seq = (i + 1) * base;
      }
    };

    let newSeq = 0;

    if (insertAfter) {
      // Append at end
      if (idx === allData.length - 1) {
        newSeq = focusedDatum.seq + base;
      } else {
        const next = allData[idx + 1];
        const gap = next.seq - focusedDatum.seq;
        if (gap >= 2) {
          newSeq = NumberUtils.Mid(focusedDatum.seq, next.seq);
        } else {
          // No integer room; normalize then take the middle
          //resequence();
          this.resetAllSeq(allData, updateSeqFunc, allData[0].seq);
          newSeq = NumberUtils.Mid(allData[idx].seq, allData[idx + 1].seq);
        }
      }
    } else {
      // Insert before
      if (idx === 0) {
        newSeq = focusedDatum.seq - base;
      } else {
        const prev = allData[idx - 1];
        const gap = focusedDatum.seq - prev.seq;
        if (gap >= 2) {
          newSeq = NumberUtils.Mid(prev.seq, focusedDatum.seq);
        } else {
          //resequence();          
          this.resetAllSeq(allData, updateSeqFunc, allData[0].seq);
          newSeq = NumberUtils.Mid(allData[idx - 1].seq, allData[idx].seq);
        }
      }
    }

    return newSeq;
  }
}
