import { createRequire as __vfCreateRequire } from 'node:module';
const require = __vfCreateRequire(import.meta.url);

// src/engine/project.ts
var generateId = () => {
  return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
};
function createDefaultState(name = "Untitled Project") {
  return {
    project: {
      id: generateId(),
      name,
      createdAt: /* @__PURE__ */ new Date(),
      updatedAt: /* @__PURE__ */ new Date(),
      aspectRatio: "16:9",
      frameRate: 30,
      duration: 0
    },
    tracks: [
      {
        id: "video-track-1",
        name: "Video 1",
        type: "video",
        order: 1,
        isMuted: false,
        isLocked: false,
        isVisible: true
      },
      {
        id: "audio-track-1",
        name: "Audio 1",
        type: "audio",
        order: 0,
        isMuted: false,
        isLocked: false,
        isVisible: true
      }
    ],
    clips: [],
    sources: [],
    transitions: [],
    currentTime: 0,
    isPlaying: false,
    zoom: 50,
    scrollX: 0,
    selectedClipIds: [],
    selectedTrackId: null
  };
}
var Project = class _Project {
  state;
  filePath = null;
  constructor(name) {
    this.state = createDefaultState(name);
  }
  /** Get current state (immutable copy) */
  getState() {
    return structuredClone(this.state);
  }
  /** Get project metadata */
  getMeta() {
    return { ...this.state.project };
  }
  /** Get file path */
  getFilePath() {
    return this.filePath;
  }
  // ============ Project Operations ============
  setName(name) {
    this.state.project.name = name;
    this.state.project.updatedAt = /* @__PURE__ */ new Date();
  }
  setAspectRatio(ratio) {
    this.state.project.aspectRatio = ratio;
    this.state.project.updatedAt = /* @__PURE__ */ new Date();
  }
  setFrameRate(fps) {
    this.state.project.frameRate = fps;
    this.state.project.updatedAt = /* @__PURE__ */ new Date();
  }
  // ============ Media Source Operations ============
  addSource(source) {
    const newSource = { ...source, id: generateId() };
    this.state.sources.push(newSource);
    return newSource;
  }
  removeSource(id) {
    const index = this.state.sources.findIndex((s) => s.id === id);
    if (index === -1) return false;
    this.state.sources.splice(index, 1);
    this.state.clips = this.state.clips.filter((c) => c.sourceId !== id);
    this.calculateDuration();
    return true;
  }
  getSource(id) {
    return this.state.sources.find((s) => s.id === id);
  }
  getSources() {
    return [...this.state.sources];
  }
  // ============ Track Operations ============
  addTrack(track) {
    const newTrack = { ...track, id: generateId() };
    this.state.tracks.push(newTrack);
    return newTrack;
  }
  removeTrack(id) {
    const index = this.state.tracks.findIndex((t) => t.id === id);
    if (index === -1) return false;
    this.state.tracks.splice(index, 1);
    this.state.clips = this.state.clips.filter((c) => c.trackId !== id);
    this.calculateDuration();
    return true;
  }
  updateTrack(id, updates) {
    const track = this.state.tracks.find((t) => t.id === id);
    if (!track) return false;
    Object.assign(track, updates);
    return true;
  }
  getTrack(id) {
    return this.state.tracks.find((t) => t.id === id);
  }
  getTracks() {
    return [...this.state.tracks];
  }
  getTracksByType(type) {
    return this.state.tracks.filter((t) => t.type === type);
  }
  // ============ Clip Operations ============
  addClip(clip) {
    const newClip = {
      ...clip,
      id: generateId(),
      effects: []
    };
    this.state.clips.push(newClip);
    this.calculateDuration();
    return newClip;
  }
  removeClip(id) {
    const index = this.state.clips.findIndex((c) => c.id === id);
    if (index === -1) return false;
    this.state.clips.splice(index, 1);
    this.state.selectedClipIds = this.state.selectedClipIds.filter((cid) => cid !== id);
    this.calculateDuration();
    return true;
  }
  updateClip(id, updates) {
    const clip = this.state.clips.find((c) => c.id === id);
    if (!clip) return false;
    Object.assign(clip, updates);
    this.calculateDuration();
    return true;
  }
  moveClip(id, trackId, startTime) {
    const clip = this.state.clips.find((c) => c.id === id);
    if (!clip) return false;
    clip.trackId = trackId;
    clip.startTime = Math.max(0, startTime);
    this.calculateDuration();
    return true;
  }
  trimClipStart(id, newStartTime) {
    const clip = this.state.clips.find((c) => c.id === id);
    if (!clip) return false;
    const delta = newStartTime - clip.startTime;
    clip.startTime = newStartTime;
    clip.sourceStartOffset += delta;
    clip.duration -= delta;
    this.calculateDuration();
    return true;
  }
  trimClipEnd(id, newDuration) {
    const clip = this.state.clips.find((c) => c.id === id);
    if (!clip) return false;
    clip.duration = Math.max(0.1, newDuration);
    clip.sourceEndOffset = clip.sourceStartOffset + clip.duration;
    this.calculateDuration();
    return true;
  }
  getClip(id) {
    return this.state.clips.find((c) => c.id === id);
  }
  getClips() {
    return [...this.state.clips];
  }
  getClipsByTrack(trackId) {
    return this.state.clips.filter((c) => c.trackId === trackId);
  }
  /**
   * Split a clip at a specific time, creating two clips
   * @param id Clip ID to split
   * @param splitTime Time relative to clip start (not timeline time)
   * @returns [firstClip, secondClip] or null if failed
   */
  splitClip(id, splitTime) {
    const clip = this.state.clips.find((c) => c.id === id);
    if (!clip) return null;
    if (splitTime <= 0 || splitTime >= clip.duration) {
      return null;
    }
    const secondClip = {
      id: generateId(),
      sourceId: clip.sourceId,
      trackId: clip.trackId,
      startTime: clip.startTime + splitTime,
      duration: clip.duration - splitTime,
      sourceStartOffset: clip.sourceStartOffset + splitTime,
      sourceEndOffset: clip.sourceEndOffset,
      effects: []
      // Effects don't transfer to split clips
    };
    clip.duration = splitTime;
    clip.sourceEndOffset = clip.sourceStartOffset + splitTime;
    this.state.clips.push(secondClip);
    this.calculateDuration();
    return [clip, secondClip];
  }
  /**
   * Duplicate a clip
   * @param id Clip ID to duplicate
   * @param offsetTime Optional time offset for the duplicate (default: place after original)
   * @returns The duplicated clip or null if failed
   */
  duplicateClip(id, offsetTime) {
    const clip = this.state.clips.find((c) => c.id === id);
    if (!clip) return null;
    const newStartTime = offsetTime ?? clip.startTime + clip.duration;
    const duplicatedClip = {
      id: generateId(),
      sourceId: clip.sourceId,
      trackId: clip.trackId,
      startTime: newStartTime,
      duration: clip.duration,
      sourceStartOffset: clip.sourceStartOffset,
      sourceEndOffset: clip.sourceEndOffset,
      effects: clip.effects.map((e) => ({
        ...e,
        id: generateId()
      }))
    };
    this.state.clips.push(duplicatedClip);
    this.calculateDuration();
    return duplicatedClip;
  }
  // ============ Effect Operations ============
  addEffect(clipId, effect) {
    const clip = this.state.clips.find((c) => c.id === clipId);
    if (!clip) return null;
    const newEffect = { ...effect, id: generateId() };
    clip.effects.push(newEffect);
    return newEffect;
  }
  removeEffect(clipId, effectId) {
    const clip = this.state.clips.find((c) => c.id === clipId);
    if (!clip) return false;
    const index = clip.effects.findIndex((e) => e.id === effectId);
    if (index === -1) return false;
    clip.effects.splice(index, 1);
    return true;
  }
  updateEffect(clipId, effectId, updates) {
    const clip = this.state.clips.find((c) => c.id === clipId);
    if (!clip) return false;
    const effect = clip.effects.find((e) => e.id === effectId);
    if (!effect) return false;
    Object.assign(effect, updates);
    return true;
  }
  // ============ Transition Operations ============
  addTransition(transition) {
    const newTransition = { ...transition, id: generateId() };
    this.state.transitions.push(newTransition);
    return newTransition;
  }
  removeTransition(id) {
    const index = this.state.transitions.findIndex((t) => t.id === id);
    if (index === -1) return false;
    this.state.transitions.splice(index, 1);
    return true;
  }
  getTransitions() {
    return [...this.state.transitions];
  }
  // ============ Duration Calculation ============
  calculateDuration() {
    const maxEndTime = this.state.clips.reduce((max, clip) => {
      const endTime = clip.startTime + clip.duration;
      return Math.max(max, endTime);
    }, 0);
    this.state.project.duration = maxEndTime;
  }
  getDuration() {
    return this.state.project.duration;
  }
  // ============ Serialization ============
  toJSON() {
    const state = this.getState();
    return {
      version: "1.0.0",
      state: {
        project: state.project,
        tracks: state.tracks,
        clips: state.clips,
        sources: state.sources,
        transitions: state.transitions
      }
    };
  }
  static fromJSON(data) {
    const project = new _Project();
    data.state.project.createdAt = new Date(data.state.project.createdAt);
    data.state.project.updatedAt = new Date(data.state.project.updatedAt);
    project.state = {
      ...data.state,
      currentTime: 0,
      isPlaying: false,
      zoom: 50,
      scrollX: 0,
      selectedClipIds: [],
      selectedTrackId: null
    };
    return project;
  }
  setFilePath(path) {
    this.filePath = path;
  }
  // ============ Summary ============
  getSummary() {
    return {
      name: this.state.project.name,
      duration: this.state.project.duration,
      aspectRatio: this.state.project.aspectRatio,
      frameRate: this.state.project.frameRate,
      trackCount: this.state.tracks.length,
      clipCount: this.state.clips.length,
      sourceCount: this.state.sources.length
    };
  }
};
export {
  Project,
  generateId
};
