import { KGCommand } from '../KGCommand';
import { KGCore } from '../../KGCore';
import { KGProject, type KeySignature } from '../../KGProject';
import type { TimeSignature } from '../../../types/projectTypes';

/**
 * Interface defining properties that can be updated on a project
 */
export interface ProjectUpdateProperties {
  name?: string;
  maxBars?: number;
  currentBars?: number;
  bpm?: number;
  timeSignature?: TimeSignature;
  keySignature?: KeySignature;
  selectedMode?: string;
}

/**
 * Command to update project properties
 * Handles updating project name, BPM, time signature, etc. with undo support
 */
export class ChangeProjectPropertyCommand extends KGCommand {
  private newProperties: ProjectUpdateProperties;
  private originalProperties: ProjectUpdateProperties = {};
  private targetProject: KGProject | null = null;
  private changedProperties: Set<keyof ProjectUpdateProperties> = new Set();

  constructor(properties: ProjectUpdateProperties) {
    super();
    this.newProperties = properties;
  }

  execute(): void {
    const core = KGCore.instance();
    this.targetProject = core.getCurrentProject();

    // Store original properties for undo
    this.originalProperties = {
      name: this.targetProject.getName(),
      maxBars: this.targetProject.getMaxBars(),
      currentBars: this.targetProject.getCurrentBars(),
      bpm: this.targetProject.getBpm(),
      timeSignature: { ...this.targetProject.getTimeSignature() }, // Create a copy
      keySignature: this.targetProject.getKeySignature(),
      selectedMode: this.targetProject.getSelectedMode(),
    };

    // Apply updates and track what actually changes
    const updatedProperties: string[] = [];

    // Update name
    if (this.newProperties.name !== undefined && this.newProperties.name !== this.originalProperties.name) {
      this.targetProject.setName(this.newProperties.name);
      this.changedProperties.add('name');
      updatedProperties.push(`name: "${this.originalProperties.name}" → "${this.newProperties.name}"`);
    }

    // Update maxBars
    if (this.newProperties.maxBars !== undefined && this.newProperties.maxBars !== this.originalProperties.maxBars) {
      this.targetProject.setMaxBars(this.newProperties.maxBars);
      this.changedProperties.add('maxBars');
      updatedProperties.push(`maxBars: ${this.originalProperties.maxBars} → ${this.newProperties.maxBars}`);
    }

    // Update currentBars
    if (this.newProperties.currentBars !== undefined && this.newProperties.currentBars !== this.originalProperties.currentBars) {
      this.targetProject.setCurrentBars(this.newProperties.currentBars);
      this.changedProperties.add('currentBars');
      updatedProperties.push(`currentBars: ${this.originalProperties.currentBars} → ${this.newProperties.currentBars}`);
    }

    // Update BPM
    if (this.newProperties.bpm !== undefined && this.newProperties.bpm !== this.originalProperties.bpm) {
      this.targetProject.setBpm(this.newProperties.bpm);
      this.changedProperties.add('bpm');
      updatedProperties.push(`bpm: ${this.originalProperties.bpm} → ${this.newProperties.bpm}`);
    }

    // Update time signature
    if (this.newProperties.timeSignature !== undefined) {
      const originalTS = this.originalProperties.timeSignature!;
      const newTS = this.newProperties.timeSignature;
      
      // Compare time signatures
      if (originalTS.numerator !== newTS.numerator || originalTS.denominator !== newTS.denominator) {
        this.targetProject.setTimeSignature(newTS);
        this.changedProperties.add('timeSignature');
        updatedProperties.push(`timeSignature: ${originalTS.numerator}/${originalTS.denominator} → ${newTS.numerator}/${newTS.denominator}`);
      }
    }

    // Update key signature
    if (this.newProperties.keySignature !== undefined && this.newProperties.keySignature !== this.originalProperties.keySignature) {
      this.targetProject.setKeySignature(this.newProperties.keySignature);
      this.changedProperties.add('keySignature');
      updatedProperties.push(`keySignature: "${this.originalProperties.keySignature}" → "${this.newProperties.keySignature}"`);
    }

    // Update selected mode
    if (this.newProperties.selectedMode !== undefined && this.newProperties.selectedMode !== this.originalProperties.selectedMode) {
      this.targetProject.setSelectedMode(this.newProperties.selectedMode);
      this.changedProperties.add('selectedMode');
      updatedProperties.push(`selectedMode: "${this.originalProperties.selectedMode}" → "${this.newProperties.selectedMode}"`);
    }

    if (updatedProperties.length > 0) {
      console.log(`Updated project: ${updatedProperties.join(', ')}`);
    } else {
      console.log('No changes applied to project');
    }
  }

  undo(): void {
    if (!this.targetProject) {
      throw new Error('Cannot undo: no project was updated');
    }

    // Only restore properties that were actually changed
    const restoredProperties: string[] = [];

    // Restore name (only if it was changed)
    if (this.changedProperties.has('name') && this.originalProperties.name !== undefined) {
      this.targetProject.setName(this.originalProperties.name);
      restoredProperties.push(`name: "${this.originalProperties.name}"`);
    }

    // Restore maxBars (only if it was changed)
    if (this.changedProperties.has('maxBars') && this.originalProperties.maxBars !== undefined) {
      this.targetProject.setMaxBars(this.originalProperties.maxBars);
      restoredProperties.push(`maxBars: ${this.originalProperties.maxBars}`);
    }

    // Restore currentBars (only if it was changed)
    if (this.changedProperties.has('currentBars') && this.originalProperties.currentBars !== undefined) {
      this.targetProject.setCurrentBars(this.originalProperties.currentBars);
      restoredProperties.push(`currentBars: ${this.originalProperties.currentBars}`);
    }

    // Restore BPM (only if it was changed)
    if (this.changedProperties.has('bpm') && this.originalProperties.bpm !== undefined) {
      this.targetProject.setBpm(this.originalProperties.bpm);
      restoredProperties.push(`bpm: ${this.originalProperties.bpm}`);
    }

    // Restore time signature (only if it was changed)
    if (this.changedProperties.has('timeSignature') && this.originalProperties.timeSignature !== undefined) {
      this.targetProject.setTimeSignature(this.originalProperties.timeSignature);
      const ts = this.originalProperties.timeSignature;
      restoredProperties.push(`timeSignature: ${ts.numerator}/${ts.denominator}`);
    }

    // Restore key signature (only if it was changed)
    if (this.changedProperties.has('keySignature') && this.originalProperties.keySignature !== undefined) {
      this.targetProject.setKeySignature(this.originalProperties.keySignature);
      restoredProperties.push(`keySignature: "${this.originalProperties.keySignature}"`);
    }

    // Restore selected mode (only if it was changed)
    if (this.changedProperties.has('selectedMode') && this.originalProperties.selectedMode !== undefined) {
      this.targetProject.setSelectedMode(this.originalProperties.selectedMode);
      restoredProperties.push(`selectedMode: "${this.originalProperties.selectedMode}"`);
    }

    console.log(`Restored project: ${restoredProperties.join(', ')}`);
  }

  getDescription(): string {
    const updatedProps: string[] = [];

    if (this.newProperties.name !== undefined) {
      updatedProps.push('name');
    }
    if (this.newProperties.maxBars !== undefined) {
      updatedProps.push('maxBars');
    }
    if (this.newProperties.currentBars !== undefined) {
      updatedProps.push('currentBars');
    }
    if (this.newProperties.bpm !== undefined) {
      updatedProps.push('BPM');
    }
    if (this.newProperties.timeSignature !== undefined) {
      updatedProps.push('time signature');
    }
    if (this.newProperties.keySignature !== undefined) {
      updatedProps.push('key signature');
    }
    if (this.newProperties.selectedMode !== undefined) {
      updatedProps.push('selected mode');
    }

    if (updatedProps.length === 1) {
      return `Change project ${updatedProps[0]}`;
    } else if (updatedProps.length > 1) {
      return `Change project properties (${updatedProps.join(', ')})`;
    }

    return `Change project properties`;
  }

  /**
   * Get the new properties being applied
   */
  public getNewProperties(): ProjectUpdateProperties {
    return this.newProperties;
  }

  /**
   * Get the original properties (only available after execute)
   */
  public getOriginalProperties(): ProjectUpdateProperties {
    return this.originalProperties;
  }

  /**
   * Get the target project instance (only available after execute)
   */
  public getTargetProject(): KGProject | null {
    return this.targetProject;
  }

  /**
   * Get the properties that were actually changed (only available after execute)
   */
  public getChangedProperties(): Set<keyof ProjectUpdateProperties> {
    return new Set(this.changedProperties);
  }
}