// Tests for sessions store
// TRACES: UR-010 | DR-037
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { get } from "svelte/store";
import type { Session } from "$lib/api/types";

// Mock the auth store
vi.mock("./auth", () => ({
  auth: {
    getRepository: vi.fn(() => ({
      sessions: {
        getSessions: vi.fn().mockResolvedValue([]),
      },
    })),
  },
}));

describe("sessions store", () => {
  // Mock session data
  const mockSession1: Session = {
    id: "session-1",
    userId: "user-1",
    userName: "Test User",
    client: "Jellyfin Web",
    deviceName: "Chrome Browser",
    deviceId: "device-1",
    applicationVersion: "10.8.0",
    isActive: true,
    supportsMediaControl: true,
    supportsRemoteControl: true,
    playState: {
      positionTicks: 1000000000,
      canSeek: true,
      isPaused: false,
      isMuted: false,
      volumeLevel: 75,
      repeatMode: "RepeatNone",
      shuffleMode: "Sorted",
    },
    nowPlayingItem: {
      id: "item-1",
      name: "Test Song",
      Type: "Audio",
      runTimeTicks: null,
      album: null,
      albumId: null,
      albumArtist: null,
      artists: null,
      imageTags: null,
      primaryImageTag: null,
      albumPrimaryImageTag: null,
    },
    playableMediaTypes: ["Audio", "Video"],
    supportedCommands: ["PlayPause", "Stop", "Seek", "NextTrack", "PreviousTrack"],
  };

  const mockSession2: Session = {
    id: "session-2",
    userId: "user-1",
    userName: "Test User",
    client: "Jellyfin Mobile",
    deviceName: "iPhone",
    deviceId: "device-2",
    applicationVersion: "1.0.0",
    isActive: true,
    supportsMediaControl: false,
    supportsRemoteControl: false,
    playState: null,
    nowPlayingItem: null,
    playableMediaTypes: [],
    supportedCommands: [],
  };

  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();

    // Ensure window.setInterval and window.clearInterval are available
    if (typeof window !== 'undefined') {
      global.window = window as any;
    }
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  describe("initial state", () => {
    it("should have empty sessions initially", async () => {
      // Import dynamically to get fresh store instance
      const { sessions } = await import("./sessions");
      const state = get(sessions);

      expect(state.sessions).toEqual([]);
      expect(state.selectedSessionId).toBeNull();
      expect(state.isLoading).toBe(false);
      expect(state.error).toBeNull();
      expect(state.lastUpdated).toBeNull();
    });
  });

  describe("selectSession", () => {
    it("should select a session by ID", async () => {
      const { sessions } = await import("./sessions");

      sessions.selectSession("session-123");
      const state = get(sessions);

      expect(state.selectedSessionId).toBe("session-123");
    });

    it("should allow deselecting by passing null", async () => {
      const { sessions } = await import("./sessions");

      sessions.selectSession("session-123");
      sessions.selectSession(null);
      const state = get(sessions);

      expect(state.selectedSessionId).toBeNull();
    });
  });

  describe("derived stores", () => {
    it("activeSessions should filter sessions with nowPlayingItem", async () => {
      // This test would require mocking the store's internal state
      // For a real implementation, you'd need to:
      // 1. Mock the auth.getRepository().sessions.getSessions() call
      // 2. Call sessions.refresh()
      // 3. Then check the derived store

      // Placeholder test structure:
      const { activeSessions } = await import("./sessions");
      const active = get(activeSessions);

      // Initially empty
      expect(active).toEqual([]);
    });

    it("selectedSession should return the selected session or null", async () => {
      const { selectedSession } = await import("./sessions");
      const selected = get(selectedSession);

      // Initially null
      expect(selected).toBeNull();
    });

    it("controllableSessions should filter sessions with supportsRemoteControl", async () => {
      const { controllableSessions } = await import("./sessions");
      const controllable = get(controllableSessions);

      // Initially empty
      expect(controllable).toEqual([]);
    });
  });

  describe("refresh", () => {
    it("should expose a refresh method for manual session fetching", async () => {
      const { sessions } = await import("./sessions");

      // The store uses event-driven updates via Tauri events,
      // but also exposes refresh() for manual/on-demand fetching
      expect(typeof sessions.refresh).toBe("function");
    });
  });

  describe("command methods", () => {
    it("sendPlayPause should call API and refresh", async () => {
      const { sessions } = await import("./sessions");

      // These would need proper mocking of the auth.getRepository() chain
      // For now, we're documenting the expected behavior

      // Mock implementation would be:
      // vi.spyOn(auth, 'getRepository').mockReturnValue({
      //   sessions: {
      //     sendCommand: vi.fn().mockResolvedValue(undefined)
      //   }
      // });

      // await sessions.sendPlayPause('session-123');
      // expect(mockSendCommand).toHaveBeenCalledWith('session-123', 'PlayPause');
    });

    it("sendStop should call API and refresh", async () => {
      const { sessions } = await import("./sessions");

      // Similar structure to sendPlayPause test
      // Would verify sendCommand is called with 'Stop'
    });

    it("sendNext should call API and refresh", async () => {
      const { sessions } = await import("./sessions");

      // Would verify sendNextTrack is called
    });

    it("sendPrevious should call API and refresh", async () => {
      const { sessions } = await import("./sessions");

      // Would verify sendPreviousTrack is called
    });

    it("sendSeek should call API without immediate refresh", async () => {
      const { sessions } = await import("./sessions");

      // Would verify seek is called but refresh is NOT called
      // (to avoid UI lag during seeking)
    });

    it("sendVolume should call API without immediate refresh", async () => {
      const { sessions } = await import("./sessions");

      // Would verify setVolume is called but refresh is NOT called
      // (to avoid UI lag during volume changes)
    });

    it("sendToggleMute should call API and refresh", async () => {
      const { sessions } = await import("./sessions");

      // Would verify toggleMute is called and refresh is called
    });

    it("playOnSession should call API and refresh", async () => {
      const { sessions } = await import("./sessions");

      // Would verify playOnSession is called with correct parameters
    });
  });

  describe("error handling", () => {
    it("should set error state when refresh fails", async () => {
      const { sessions } = await import("./sessions");

      // Mock auth.getRepository() to throw an error
      // const error = new Error("Network error");
      // Mock implementation would set up the error

      // await sessions.refresh();
      // const state = get(sessions);
      // expect(state.error).toBe("Network error");
      // expect(state.isLoading).toBe(false);
    });

    it("should log errors to console when commands fail", async () => {
      const { sessions } = await import("./sessions");

      const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});

      // Mock a failing command
      // await expect(sessions.sendPlayPause('session-1')).rejects.toThrow();
      // expect(consoleSpy).toHaveBeenCalled();

      consoleSpy.mockRestore();
    });
  });
});
