import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';

// Stub localStorage before auth store initialization (imported transitively by generation store)
vi.stubGlobal('localStorage', {
  getItem: vi.fn(() => null),
  setItem: vi.fn(),
  removeItem: vi.fn(),
  clear: vi.fn(),
  length: 0,
  key: vi.fn(() => null),
});

const mockGenerate = vi.fn().mockResolvedValue({
  data: {
    task_id: 'test-uuid-001',
    ark_task_id: '',
    status: 'queued',
    estimated_time: 120,
    seconds_consumed: 5,
    remaining_seconds_today: 595,
  },
});

const mockUpload = vi.fn().mockResolvedValue({
  data: { url: 'https://tos.example.com/image/test.jpg', type: 'image', filename: 'test.jpg', size: 1234 },
});

const mockGetTasks = vi.fn().mockResolvedValue({ data: { results: [] } });
const mockGetTaskStatus = vi.fn().mockResolvedValue({
  data: { task_id: 'test-uuid-001', status: 'queued', result_url: '', error_message: '', reference_urls: [] },
});

// Mock auth API to prevent network calls from auth store
vi.mock('../../src/lib/api', () => ({
  authApi: {
    login: vi.fn(),
    register: vi.fn(),
    refreshToken: vi.fn(),
    getMe: vi.fn(),
  },
  videoApi: {
    generate: (...args: unknown[]) => mockGenerate(...args),
    getTasks: (...args: unknown[]) => mockGetTasks(...args),
    getTaskStatus: (...args: unknown[]) => mockGetTaskStatus(...args),
  },
  mediaApi: {
    upload: (...args: unknown[]) => mockUpload(...args),
  },
  adminApi: { getStats: vi.fn(), getUserRankings: vi.fn(), updateUserQuota: vi.fn() },
  default: { interceptors: { request: { use: vi.fn() }, response: { use: vi.fn() } } },
}));

vi.mock('../../src/components/Toast', () => ({
  showToast: vi.fn(),
}));

// Mock fetchUserInfo to prevent network calls
vi.mock('../../src/store/auth', async (importOriginal) => {
  const original = await importOriginal() as Record<string, unknown>;
  return {
    ...original,
    useAuthStore: Object.assign(
      (selector: (s: Record<string, unknown>) => unknown) =>
        selector({ fetchUserInfo: vi.fn() }),
      { getState: () => ({ fetchUserInfo: vi.fn() }) }
    ),
  };
});

import { useGenerationStore } from '../../src/store/generation';
import { useInputBarStore } from '../../src/store/inputBar';

function createMockFile(name: string, type: string): File {
  return new File(['mock'], name, { type });
}

describe('Generation Store', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    useInputBarStore.getState().reset();
    useGenerationStore.setState({ tasks: [], isLoading: false });
    mockGenerate.mockClear();
    mockUpload.mockClear();
    mockGetTasks.mockClear();
    mockGetTaskStatus.mockClear();
    // Reset generate mock to return unique IDs
    let callCount = 0;
    mockGenerate.mockImplementation(() => {
      callCount++;
      return Promise.resolve({
        data: {
          task_id: `test-uuid-${String(callCount).padStart(3, '0')}`,
          ark_task_id: '',
          status: 'queued',
          estimated_time: 120,
          seconds_consumed: 5,
          remaining_seconds_today: 595,
        },
      });
    });
  });

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

  describe('addTask', () => {
    it('should return null when canSubmit is false', async () => {
      const result = await useGenerationStore.getState().addTask();
      expect(result).toBeNull();
    });

    it('should create a task when prompt has text', async () => {
      vi.useRealTimers();
      useInputBarStore.getState().setPrompt('test prompt');
      const id = await useGenerationStore.getState().addTask();
      expect(id).not.toBeNull();
      expect(useGenerationStore.getState().tasks).toHaveLength(1);
      vi.useFakeTimers();
    });

    it('should create task with correct properties', async () => {
      useInputBarStore.getState().setPrompt('a beautiful scene');
      useInputBarStore.getState().setModel('seedance_2.0_fast');
      useInputBarStore.getState().setAspectRatio('16:9');
      useInputBarStore.getState().setDuration(10);

      // Placeholder task is created synchronously
      const promise = useGenerationStore.getState().addTask();
      const task = useGenerationStore.getState().tasks[0];
      expect(task.prompt).toBe('a beautiful scene');
      expect(task.model).toBe('seedance_2.0_fast');
      expect(task.aspectRatio).toBe('16:9');
      expect(task.duration).toBe(10);
      expect(task.mode).toBe('universal');
      expect(task.status).toBe('generating');

      vi.useRealTimers();
      await promise;
      vi.useFakeTimers();
    });

    it('should snapshot references in universal mode', async () => {
      useInputBarStore.getState().addReferences([
        createMockFile('img1.jpg', 'image/jpeg'),
        createMockFile('img2.jpg', 'image/jpeg'),
      ]);

      const promise = useGenerationStore.getState().addTask();
      const task = useGenerationStore.getState().tasks[0];
      expect(task.references).toHaveLength(2);
      expect(task.references[0].type).toBe('image');

      vi.useRealTimers();
      await promise;
      vi.useFakeTimers();
    });

    it('should snapshot frames in keyframe mode', async () => {
      useInputBarStore.getState().switchMode('keyframe');
      useInputBarStore.getState().setFirstFrame(createMockFile('first.jpg', 'image/jpeg'));
      useInputBarStore.getState().setLastFrame(createMockFile('last.jpg', 'image/jpeg'));

      const promise = useGenerationStore.getState().addTask();
      const task = useGenerationStore.getState().tasks[0];
      expect(task.references).toHaveLength(2);
      expect(task.references[0].label).toBe('首帧');
      expect(task.references[1].label).toBe('尾帧');

      vi.useRealTimers();
      await promise;
      vi.useFakeTimers();
    });

    it('should clear input after submit', async () => {
      useInputBarStore.getState().setPrompt('test');
      useInputBarStore.getState().addReferences([createMockFile('img.jpg', 'image/jpeg')]);

      const promise = useGenerationStore.getState().addTask();
      // Input is cleared synchronously after placeholder creation
      expect(useInputBarStore.getState().prompt).toBe('');
      expect(useInputBarStore.getState().references).toHaveLength(0);

      vi.useRealTimers();
      await promise;
      vi.useFakeTimers();
    });

    it('should prepend new tasks (newest first)', async () => {
      vi.useRealTimers();
      useInputBarStore.getState().setPrompt('first');
      await useGenerationStore.getState().addTask();

      useInputBarStore.getState().setPrompt('second');
      await useGenerationStore.getState().addTask();

      const tasks = useGenerationStore.getState().tasks;
      expect(tasks).toHaveLength(2);
      expect(tasks[0].prompt).toBe('second');
      expect(tasks[1].prompt).toBe('first');
      vi.useFakeTimers();
    });

    it('should call videoApi.generate with correct data', async () => {
      vi.useRealTimers();
      useInputBarStore.getState().setPrompt('test prompt');
      useInputBarStore.getState().setModel('seedance_2.0');
      useInputBarStore.getState().setAspectRatio('16:9');
      useInputBarStore.getState().setDuration(10);

      await useGenerationStore.getState().addTask();

      expect(mockGenerate).toHaveBeenCalledWith({
        prompt: 'test prompt',
        mode: 'universal',
        model: 'seedance_2.0',
        aspect_ratio: '16:9',
        duration: 10,
        references: [],
      });
      vi.useFakeTimers();
    });

    it('should upload files to TOS before generating', async () => {
      vi.useRealTimers();
      useInputBarStore.getState().addReferences([
        createMockFile('img.jpg', 'image/jpeg'),
      ]);

      await useGenerationStore.getState().addTask();

      expect(mockUpload).toHaveBeenCalledTimes(1);
      expect(mockGenerate).toHaveBeenCalledTimes(1);
      vi.useFakeTimers();
    });
  });

  describe('removeTask', () => {
    it('should remove a task by id', async () => {
      vi.useRealTimers();
      useInputBarStore.getState().setPrompt('test');
      const id = await useGenerationStore.getState().addTask();

      expect(useGenerationStore.getState().tasks).toHaveLength(1);
      useGenerationStore.getState().removeTask(id!);
      expect(useGenerationStore.getState().tasks).toHaveLength(0);
      vi.useFakeTimers();
    });

    it('should not affect other tasks', async () => {
      vi.useRealTimers();
      useInputBarStore.getState().setPrompt('first');
      const id1 = await useGenerationStore.getState().addTask();

      useInputBarStore.getState().setPrompt('second');
      await useGenerationStore.getState().addTask();

      useGenerationStore.getState().removeTask(id1!);
      expect(useGenerationStore.getState().tasks).toHaveLength(1);
      expect(useGenerationStore.getState().tasks[0].prompt).toBe('second');
      vi.useFakeTimers();
    });
  });

  describe('reEdit', () => {
    it('should restore prompt from task', async () => {
      vi.useRealTimers();
      useInputBarStore.getState().setPrompt('original prompt');
      const id = await useGenerationStore.getState().addTask();

      // Input is cleared after submit
      expect(useInputBarStore.getState().prompt).toBe('');

      useGenerationStore.getState().reEdit(id!);
      expect(useInputBarStore.getState().prompt).toBe('original prompt');
      vi.useFakeTimers();
    });

    it('should restore settings from task', async () => {
      vi.useRealTimers();
      useInputBarStore.getState().setPrompt('test');
      useInputBarStore.getState().setAspectRatio('16:9');
      useInputBarStore.getState().setDuration(10);
      const id = await useGenerationStore.getState().addTask();

      // Reset to defaults
      useInputBarStore.getState().setAspectRatio('21:9');
      useInputBarStore.getState().setDuration(15);

      useGenerationStore.getState().reEdit(id!);
      expect(useInputBarStore.getState().aspectRatio).toBe('16:9');
      expect(useInputBarStore.getState().duration).toBe(10);
      vi.useFakeTimers();
    });

    it('should do nothing for non-existent task', () => {
      useInputBarStore.getState().setPrompt('existing');
      useGenerationStore.getState().reEdit('non_existent_id');
      expect(useInputBarStore.getState().prompt).toBe('existing');
    });
  });

  describe('regenerate', () => {
    it('should create a new task based on existing one', async () => {
      vi.useRealTimers();
      useInputBarStore.getState().setPrompt('test');
      const originalId = await useGenerationStore.getState().addTask();

      useGenerationStore.getState().regenerate(originalId!);
      // Allow time for the async regenerate to complete
      await new Promise(resolve => setTimeout(resolve, 100));

      const tasks = useGenerationStore.getState().tasks;
      expect(tasks.length).toBeGreaterThanOrEqual(2);
      const newTask = tasks[0]; // newest first
      expect(newTask.id).not.toBe(originalId);
      expect(newTask.prompt).toBe('test');
      expect(newTask.status).toBe('generating');
      vi.useFakeTimers();
    });

    it('should do nothing for non-existent task', () => {
      useGenerationStore.getState().regenerate('non_existent');
      expect(useGenerationStore.getState().tasks).toHaveLength(0);
    });
  });

  describe('loadTasks', () => {
    it('should fetch tasks from backend on load', async () => {
      vi.useRealTimers();
      mockGetTasks.mockResolvedValueOnce({
        data: {
          results: [
            {
              id: 1,
              task_id: 'uuid-from-db',
              ark_task_id: 'ark-123',
              prompt: 'loaded prompt',
              mode: 'universal',
              model: 'seedance_2.0',
              aspect_ratio: '16:9',
              duration: 10,
              seconds_consumed: 10,
              status: 'completed',
              result_url: 'https://example.com/video.mp4',
              error_message: '',
              reference_urls: [],
              created_at: '2026-03-13T00:00:00Z',
            },
          ],
        },
      });

      await useGenerationStore.getState().loadTasks();

      const tasks = useGenerationStore.getState().tasks;
      expect(tasks).toHaveLength(1);
      expect(tasks[0].prompt).toBe('loaded prompt');
      expect(tasks[0].status).toBe('completed');
      expect(tasks[0].resultUrl).toBe('https://example.com/video.mp4');
      vi.useFakeTimers();
    });

    it('should set isLoading state', async () => {
      vi.useRealTimers();
      await useGenerationStore.getState().loadTasks();
      expect(useGenerationStore.getState().isLoading).toBe(false);
      vi.useFakeTimers();
    });
  });
});
