/**
 * ImportExecutionEngine Unit Tests
 *
 * Tests the ImportExecutionEngine which handles the actual import of WXR data.
 * Following TDD best practices: mock external dependencies, test real implementation.
 */

import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import type {
  ImportAnalysisReport,
  AnalyzedPost,
  AnalyzedMedia,
  AnalyzedCategory,
  AnalyzedTag,
} from '../../src/main/engine/ImportAnalysisEngine';
import type { WxrPost, WxrMedia, WxrSiteInfo } from '../../src/main/engine/WxrParser';

// Mock modules before importing the engine
vi.mock('../../src/main/database', () => ({
  getDatabase: vi.fn(() => ({
    getLocal: vi.fn(() => mockDb),
    getLocalClient: vi.fn(() => mockClient),
  })),
}));

vi.mock('fs/promises', () => ({
  mkdir: vi.fn().mockResolvedValue(undefined),
  writeFile: vi.fn().mockResolvedValue(undefined),
  copyFile: vi.fn().mockResolvedValue(undefined),
  readFile: vi.fn().mockResolvedValue(Buffer.from('test image data')),
  access: vi.fn().mockResolvedValue(undefined),
  stat: vi.fn().mockResolvedValue({ size: 1024 }),
}));

vi.mock('electron', () => ({
  app: {
    getPath: vi.fn(() => '/mock/user/data'),
  },
}));

vi.mock('uuid', () => ({
  v4: vi.fn(() => 'mock-uuid-' + Math.random().toString(36).substr(2, 9)),
}));

// Mock the TagEngine
const mockTagEngine = {
  setProjectContext: vi.fn(),
  createTag: vi.fn().mockImplementation(async (input: { name: string }) => ({
    id: `tag-${input.name}`,
    projectId: 'test-project',
    name: input.name.toLowerCase(),
    createdAt: new Date(),
    updatedAt: new Date(),
  })),
  getAllTags: vi.fn().mockResolvedValue([]),
};

vi.mock('../../src/main/engine/TagEngine', () => ({
  getTagEngine: vi.fn(() => mockTagEngine),
}));

// Mock the PostEngine
const mockPostEngine = {
  setProjectContext: vi.fn(),
  createPost: vi.fn().mockImplementation(async (data: any) => ({
    id: data.id || 'mock-post-id',
    projectId: data.projectId || 'test-project',
    title: data.title,
    slug: data.slug,
    content: data.content,
    excerpt: data.excerpt,
    status: data.status,
    author: data.author,
    createdAt: data.createdAt || new Date(),
    updatedAt: data.updatedAt || new Date(),
    publishedAt: data.publishedAt,
    tags: data.tags || [],
    categories: data.categories || [],
  })),
  publishPost: vi.fn().mockImplementation(async (id: string) => ({ id, status: 'published' })),
  isSlugAvailable: vi.fn().mockResolvedValue(true),
  generateUniqueSlug: vi.fn().mockImplementation(async (title: string) => `${title.toLowerCase().replace(/\s+/g, '-')}-new`),
  updateFTSIndex: vi.fn().mockResolvedValue(undefined),
};

vi.mock('../../src/main/engine/PostEngine', () => ({
  getPostEngine: vi.fn(() => mockPostEngine),
}));

// Mock the MediaEngine
const mockMediaEngine = {
  setProjectContext: vi.fn(),
  importMedia: vi.fn().mockImplementation(async (sourcePath: string, metadata?: any) => ({
    id: 'mock-media-id',
    filename: 'test.jpg',
    originalName: metadata?.originalName || 'test.jpg',
    mimeType: metadata?.mimeType || 'image/jpeg',
    size: 1024,
    width: 800,
    height: 600,
    alt: metadata?.alt,
    caption: metadata?.caption,
    createdAt: new Date(),
    updatedAt: new Date(),
    tags: metadata?.tags || [],
    linkedPostIds: metadata?.linkedPostIds || [],
  })),
  updateMedia: vi.fn().mockResolvedValue({}),
};

// Mock the PostMediaEngine
const mockPostMediaEngine = {
  setProjectContext: vi.fn(),
  linkMediaToPost: vi.fn().mockResolvedValue(undefined),
  getLinkedMediaDataForPost: vi.fn().mockResolvedValue([]),
};

vi.mock('../../src/main/engine/PostMediaEngine', () => ({
  PostMediaEngine: vi.fn(() => mockPostMediaEngine),
}));

vi.mock('../../src/main/engine/MediaEngine', () => ({
  getMediaEngine: vi.fn(() => mockMediaEngine),
}));

// Mock database - track inserts and updates for verification
const insertedPosts: any[] = [];
const insertedMedia: any[] = [];
const updatedPosts: { id: string; data: any }[] = [];

// Create a mock that tracks based on table name property
const createMockInsert = () => ({
  values: vi.fn((data: any) => {
    // The insert will be called for posts or media tables
    // We determine which based on the data structure
    if (data.slug !== undefined && data.projectId !== undefined) {
      insertedPosts.push(data);
    }
    return Promise.resolve();
  }),
});

// Create a mock that tracks post updates
const createMockUpdate = () => {
  let updateData: any = null;
  return {
    set: vi.fn((data: any) => {
      updateData = data;
      return {
        where: vi.fn((condition: any) => {
          // Track the update with the ID being updated
          // The condition typically contains the ID
          updatedPosts.push({ id: 'updated-post-id', data: updateData });
          return Promise.resolve();
        }),
      };
    }),
  };
};

const mockDb = {
  select: vi.fn(() => ({
    from: vi.fn(() => ({
      where: vi.fn(() => ({
        get: vi.fn().mockResolvedValue(null),
        all: vi.fn().mockResolvedValue([]),
      })),
    })),
  })),
  insert: vi.fn(() => createMockInsert()),
  update: vi.fn(() => createMockUpdate()),
};

const mockClient = {
  execute: vi.fn().mockResolvedValue({ rows: [] }),
};

// Import engine after mocks
import { ImportExecutionEngine, ImportExecutionResult, ImportExecutionOptions } from '../../src/main/engine/ImportExecutionEngine';

// Test fixtures
function createMockWxrPost(overrides?: Partial<WxrPost>): WxrPost {
  return {
    wpId: 1,
    title: 'Test Post',
    slug: 'test-post',
    content: '<p>Test content with [gallery ids="1,2,3"]</p>',
    excerpt: 'Test excerpt',
    pubDate: new Date('2024-01-15T10:00:00Z'),
    postDate: new Date('2024-01-15T10:00:00Z'),
    postModified: new Date('2024-01-20T15:00:00Z'),
    creator: 'admin',
    status: 'publish',
    postType: 'post',
    categories: ['Technology'],
    tags: ['JavaScript', 'TypeScript'],
    ...overrides,
  };
}

function createMockWxrMedia(overrides?: Partial<WxrMedia>): WxrMedia {
  return {
    wpId: 100,
    title: 'Test Image',
    url: 'https://example.com/wp-content/uploads/2024/01/test.jpg',
    filename: 'test.jpg',
    relativePath: '2024/01/test.jpg',
    pubDate: new Date('2024-01-15T10:00:00Z'),
    parentId: 1,
    mimeType: 'image/jpeg',
    description: 'A test image description',
    ...overrides,
  };
}

function createMockAnalyzedPost(wxrPost: WxrPost, status: 'new' | 'update' | 'conflict' | 'content-duplicate' = 'new', conflictResolution?: 'ignore' | 'overwrite' | 'import'): AnalyzedPost {
  return {
    wxrPost,
    status,
    contentHash: 'abc123',
    markdownPreview: 'Test content preview',
    conflictResolution,
  };
}

function createMockAnalyzedMedia(wxrMedia: WxrMedia, status: 'new' | 'update' | 'conflict' | 'content-duplicate' | 'missing' = 'new'): AnalyzedMedia {
  return {
    wxrMedia,
    status,
    fileHash: 'def456',
  };
}

function createMockAnalysisReport(overrides?: Partial<ImportAnalysisReport>): ImportAnalysisReport {
  return {
    sourceFile: '/path/to/export.xml',
    site: {
      title: 'Test Blog',
      link: 'https://example.com',
      description: 'A test blog',
      language: 'en-US',
    },
    analyzedAt: new Date(),
    posts: {
      total: 0,
      new: 0,
      updates: 0,
      conflicts: 0,
      contentDuplicates: 0,
      items: [],
    },
    pages: {
      total: 0,
      new: 0,
      updates: 0,
      conflicts: 0,
      contentDuplicates: 0,
      items: [],
    },
    media: {
      total: 0,
      new: 0,
      updates: 0,
      conflicts: 0,
      contentDuplicates: 0,
      missing: 0,
      items: [],
    },
    categories: [],
    tags: [],
    macros: {
      total: 0,
      mappedCount: 0,
      unmappedCount: 0,
      discovered: [],
    },
    ...overrides,
  };
}

describe('ImportExecutionEngine', () => {
  let engine: ImportExecutionEngine;

  beforeEach(() => {
    vi.clearAllMocks();
    insertedPosts.length = 0;
    insertedMedia.length = 0;
    updatedPosts.length = 0;
    engine = new ImportExecutionEngine({
      tagEngine: mockTagEngine as any,
      postEngine: mockPostEngine as any,
      mediaEngine: mockMediaEngine as any,
      postMediaEngine: mockPostMediaEngine as any,
    });
    engine.setProjectContext('test-project', '/mock/project/data');
  });

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

  describe('setProjectContext', () => {
    it('should set the project context', () => {
      engine.setProjectContext('new-project', '/new/data/path');
      expect(engine.getProjectContext()).toBe('new-project');
    });

    it('should handle project context without dataDir', () => {
      engine.setProjectContext('another-project');
      expect(engine.getProjectContext()).toBe('another-project');
    });
  });

  describe('Error Handling', () => {
    it('should handle top-level executeImport errors gracefully', async () => {
      // Make tagEngine.setProjectContext throw to simulate catastrophic failure
      mockTagEngine.setProjectContext.mockImplementationOnce(() => {
        throw new Error('Catastrophic failure');
      });
      
      const report = createMockAnalysisReport({});

      const result = await engine.executeImport(report, {});

      expect(result.success).toBe(false);
      expect(result.errors.length).toBeGreaterThan(0);
      expect(result.errors[0]).toContain('Catastrophic failure');
    });
  });

  describe('Phase 1: Tag Creation', () => {
    it('should create new tags from analysis report', async () => {
      const report = createMockAnalysisReport({
        tags: [
          { name: 'NewTag', slug: 'newtag', existsInProject: false },
          { name: 'ExistingTag', slug: 'existingtag', existsInProject: true },
        ],
        categories: [
          { name: 'NewCategory', slug: 'newcategory', existsInProject: false },
        ],
      });

      const result = await engine.executeImport(report, {});

      // Should create only new tags (not existing ones)
      expect(mockTagEngine.createTag).toHaveBeenCalledWith({ name: 'newtag' });
      expect(mockTagEngine.createTag).toHaveBeenCalledWith({ name: 'newcategory' });
      expect(mockTagEngine.createTag).toHaveBeenCalledTimes(2);
    });

    it('should not create tags that already exist', async () => {
      const report = createMockAnalysisReport({
        tags: [
          { name: 'ExistingTag', slug: 'existingtag', existsInProject: true },
        ],
      });

      await engine.executeImport(report, {});

      expect(mockTagEngine.createTag).not.toHaveBeenCalled();
    });

    it('should use mapped tag names when creating tags', async () => {
      const report = createMockAnalysisReport({
        tags: [
          { name: 'OldName', slug: 'oldname', existsInProject: false, mappedTo: 'NewName' },
        ],
      });

      await engine.executeImport(report, {});

      // Should NOT create the tag if it's mapped to an existing one
      // The mappedTo value means it should use an existing tag
      expect(mockTagEngine.createTag).not.toHaveBeenCalled();
    });

    it('should count skipped when tag creation fails (duplicate/race condition)', async () => {
      mockTagEngine.createTag.mockRejectedValueOnce(new Error('Tag already exists'));
      
      const report = createMockAnalysisReport({
        tags: [
          { name: 'NewTag', slug: 'newtag', existsInProject: false },
        ],
      });

      const result = await engine.executeImport(report, {});

      // Tag creation failed, should be counted as skipped not created
      expect(result.tags.skipped).toBe(1);
      expect(result.tags.created).toBe(0);
    });

    it('should count skipped when category creation fails', async () => {
      mockTagEngine.createTag.mockRejectedValueOnce(new Error('Category already exists'));
      
      const report = createMockAnalysisReport({
        categories: [
          { name: 'NewCategory', slug: 'newcategory', existsInProject: false },
        ],
      });

      const result = await engine.executeImport(report, {});

      expect(result.tags.skipped).toBe(1);
      expect(result.tags.created).toBe(0);
    });
  });

  describe('Phase 2: Post Import', () => {
    it('should import new posts with correct dates from WXR', async () => {
      const wxrPost = createMockWxrPost({
        postDate: new Date('2024-01-15T10:00:00Z'),
        postModified: new Date('2024-01-20T15:00:00Z'),
      });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      // Check that post was inserted into DB with correct dates
      expect(insertedPosts.length).toBe(1);
      expect(insertedPosts[0].title).toBe('Test Post');
      expect(insertedPosts[0].slug).toBe('test-post');
      expect(insertedPosts[0].createdAt).toEqual(new Date('2024-01-15T10:00:00Z'));
      expect(insertedPosts[0].updatedAt).toEqual(new Date('2024-01-20T15:00:00Z'));
    });

    it('should skip posts with conflict resolution "ignore"', async () => {
      const wxrPost = createMockWxrPost();
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'conflict', 'ignore')],
        },
      });

      const result = await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(0);
      expect(result.posts.skipped).toBe(1);
    });

    it('should update existing post for conflict resolution "overwrite"', async () => {
      const wxrPost = createMockWxrPost();
      const analyzed = createMockAnalyzedPost(wxrPost, 'conflict', 'overwrite');
      analyzed.existingPost = {
        id: 'existing-post-id',
        title: 'Existing Post',
        slug: 'test-post',
        checksum: 'old-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      const result = await engine.executeImport(report, {});

      // Should update existing post, not insert new one
      expect(insertedPosts.length).toBe(0);
      expect(updatedPosts.length).toBeGreaterThan(0);
      expect(updatedPosts[0].data.status).toBe('draft');
      expect(updatedPosts[0].data.title).toBe(wxrPost.title);
      expect(result.posts.imported).toBe(1);
    });

    it('should create new post with new slug for conflict resolution "import"', async () => {
      const wxrPost = createMockWxrPost();
      const analyzed = createMockAnalyzedPost(wxrPost, 'conflict', 'import');
      analyzed.existingPost = {
        id: 'existing-post-id',
        title: 'Existing Post',
        slug: 'test-post',
        checksum: 'old-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      await engine.executeImport(report, {});

      expect(mockPostEngine.generateUniqueSlug).toHaveBeenCalled();
      expect(insertedPosts.length).toBe(1);
    });

    it('should skip posts with status "content-duplicate"', async () => {
      const wxrPost = createMockWxrPost();
      const analyzed = createMockAnalyzedPost(wxrPost, 'content-duplicate');
      
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 1,
          items: [analyzed],
        },
      });

      const result = await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(0);
      expect(result.posts.skipped).toBe(1);
      expect(result.posts.imported).toBe(0);
    });

    it('should skip posts with status "update"', async () => {
      const wxrPost = createMockWxrPost();
      const analyzed = createMockAnalyzedPost(wxrPost, 'update');
      analyzed.existingPost = {
        id: 'existing-post-id',
        title: 'Existing Post',
        slug: 'test-post',
        checksum: 'same-content-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 0,
          updates: 1,
          conflicts: 0,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      const result = await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(0);
      expect(result.posts.skipped).toBe(1);
    });

    it('should handle empty content gracefully', async () => {
      const wxrPost = createMockWxrPost({
        content: '',
      });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      // Empty content should be preserved as empty string
    });

    it('should handle whitespace-only content gracefully', async () => {
      const wxrPost = createMockWxrPost({
        content: '   \n\t  ',
      });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
    });

    it('should include excerpt in post metadata when present', async () => {
      const wxrPost = createMockWxrPost({
        excerpt: 'This is a post excerpt',
      });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      expect(insertedPosts[0].excerpt).toBe('This is a post excerpt');
    });

    it('should include author in post metadata when present', async () => {
      const wxrPost = createMockWxrPost({
        creator: 'johndoe',
      });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      expect(insertedPosts[0].author).toBe('johndoe');
    });

    it('should include publishedAt for published posts', async () => {
      const pubDate = new Date('2024-01-15T10:00:00Z');
      const wxrPost = createMockWxrPost({
        pubDate,
      });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      expect(insertedPosts[0].publishedAt).toEqual(pubDate);
    });

    it('should not set publishedAt for overwrite draft posts', async () => {
      const wxrPost = createMockWxrPost();
      const analyzed = createMockAnalyzedPost(wxrPost, 'conflict', 'overwrite');
      analyzed.existingPost = {
        id: 'existing-post-id',
        title: 'Existing Post',
        slug: 'test-post',
        checksum: 'old-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      await engine.executeImport(report, {});

      // Should update, not insert
      expect(insertedPosts.length).toBe(0);
      expect(updatedPosts.length).toBeGreaterThan(0);
      expect(updatedPosts[0].data.status).toBe('draft');
      expect(updatedPosts[0].data.publishedAt).toBeNull();
    });

    it('should handle post without optional fields', async () => {
      const wxrPost = createMockWxrPost({
        excerpt: '',
        creator: '',
        pubDate: undefined,
        postDate: undefined,
        postModified: undefined,
      });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      // Should use current date as fallback
      expect(insertedPosts[0].createdAt).toBeInstanceOf(Date);
    });

    it('should handle dates that are ISO strings (from JSON serialization through IPC)', async () => {
      // After JSON.parse, dates become strings like "2024-01-15T10:00:00.000Z"
      const wxrPost = createMockWxrPost({
        postDate: '2023-06-20T08:30:00.000Z' as any,
        postModified: '2023-07-01T12:00:00.000Z' as any,
        pubDate: '2023-06-20T08:30:00.000Z' as any,
        status: 'publish',
      });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      expect(insertedPosts[0].createdAt).toBeInstanceOf(Date);
      expect(insertedPosts[0].createdAt).toEqual(new Date('2023-06-20T08:30:00.000Z'));
      expect(insertedPosts[0].updatedAt).toBeInstanceOf(Date);
      expect(insertedPosts[0].updatedAt).toEqual(new Date('2023-07-01T12:00:00.000Z'));
      expect(insertedPosts[0].publishedAt).toBeInstanceOf(Date);
      expect(insertedPosts[0].publishedAt).toEqual(new Date('2023-06-20T08:30:00.000Z'));
    });

    it('should handle invalid date strings gracefully', async () => {
      const wxrPost = createMockWxrPost({
        postDate: 'not-a-date' as any,
        postModified: '' as any,
        pubDate: undefined,
      });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      // Invalid dates should fall back to current date
      expect(insertedPosts[0].createdAt).toBeInstanceOf(Date);
    });

    it('should record error when post import fails', async () => {
      const wxrPost = createMockWxrPost();
      
      // Make the database insert throw an error
      const originalInsert = mockDb.insert;
      mockDb.insert = vi.fn(() => ({
        values: vi.fn().mockRejectedValue(new Error('Database error')),
      }));
      
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      const result = await engine.executeImport(report, {});

      expect(result.posts.errors).toBe(1);
      expect(result.errors.length).toBeGreaterThan(0);
      expect(result.errors[0]).toContain('Failed to import post');
      
      // Restore
      mockDb.insert = originalInsert;
    });

    it('should transform WordPress shortcodes to double bracket format', async () => {
      const wxrPost = createMockWxrPost({
        content: '<p>Check out [gallery ids="1,2"] and [video src="test.mp4"]</p>',
      });
      // Create as conflict with overwrite resolution to get a draft (content in DB)
      const analyzed = createMockAnalyzedPost(wxrPost, 'conflict', 'overwrite');
      analyzed.existingPost = {
        id: 'existing-post-id',
        title: 'Existing Post',
        slug: 'test-post',
        checksum: 'old-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      await engine.executeImport(report, {});

      // Overwrite updates existing post, not inserts
      expect(updatedPosts.length).toBeGreaterThan(0);
      // Draft posts store content in DB
      expect(updatedPosts[0].data.content).toContain('[[gallery ids="1,2"]]');
      expect(updatedPosts[0].data.content).toContain('[[video src="test.mp4"]]');
    });

    it('should not escape underscores inside macro names during markdown conversion', async () => {
      const wxrPost = createMockWxrPost({
        content: '<p>Here is a photo archive: [photo_archive]</p>',
      });
      const analyzed = createMockAnalyzedPost(wxrPost, 'conflict', 'overwrite');
      analyzed.existingPost = {
        id: 'existing-post-id',
        title: 'Existing Post',
        slug: 'test-post',
        checksum: 'old-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      await engine.executeImport(report, {});

      // Overwrite updates existing post, not inserts
      expect(updatedPosts.length).toBeGreaterThan(0);
      expect(updatedPosts[0].data.content).toContain('[[photo_archive]]');
      expect(updatedPosts[0].data.content).not.toContain('photo\\_archive');
    });

    it('should not escape underscores in macro attributes during markdown conversion', async () => {
      const wxrPost = createMockWxrPost({
        content: '<p>Show: [my_gallery type="grid_view" size="large_thumb"]</p>',
      });
      const analyzed = createMockAnalyzedPost(wxrPost, 'conflict', 'overwrite');
      analyzed.existingPost = {
        id: 'existing-post-id',
        title: 'Existing Post',
        slug: 'test-post',
        checksum: 'old-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      await engine.executeImport(report, {});

      // Overwrite updates existing post, not inserts
      expect(updatedPosts.length).toBeGreaterThan(0);
      expect(updatedPosts[0].data.content).toContain('[[my_gallery type="grid_view" size="large_thumb"]]');
    });

    it('should map tags based on analysis mappings', async () => {
      const wxrPost = createMockWxrPost({
        tags: ['OldTagName', 'NewTag'],
        categories: ['MappedCategory'],
      });
      
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
        tags: [
          { name: 'OldTagName', slug: 'oldtagname', existsInProject: false, mappedTo: 'ExistingTag' },
          { name: 'NewTag', slug: 'newtag', existsInProject: false },
        ],
        categories: [
          { name: 'MappedCategory', slug: 'mappedcategory', existsInProject: false, mappedTo: 'TargetCategory' },
        ],
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      const tags = JSON.parse(insertedPosts[0].tags);
      const categories = JSON.parse(insertedPosts[0].categories);
      expect(tags).toContain('existingtag');
      expect(tags).toContain('newtag');
      expect(categories).toContain('targetcategory');
    });

    it('should build wpId to postId mapping for media phase', async () => {
      const wxrPost = createMockWxrPost({ wpId: 42 });
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      const result = await engine.executeImport(report, {});

      expect(result.wpIdToPostId.get(42)).toBeDefined();
    });
  });

  describe('Phase 3: Media Import', () => {
    it('should import new media files', async () => {
      const wxrMedia = createMockWxrMedia();
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).toHaveBeenCalled();
    });

    it('should pass createdAt and updatedAt from WXR pubDate to MediaEngine', async () => {
      const pubDate = new Date('2020-03-15T14:30:00Z');
      const wxrMedia = createMockWxrMedia({ pubDate });
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).toHaveBeenCalledWith(
        expect.any(String),
        expect.objectContaining({
          createdAt: pubDate,
          updatedAt: pubDate,
        })
      );
    });

    it('should handle media pubDate as string (from JSON serialization)', async () => {
      const wxrMedia = createMockWxrMedia({
        pubDate: '2019-11-25T09:00:00.000Z' as any,
      });
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).toHaveBeenCalledWith(
        expect.any(String),
        expect.objectContaining({
          createdAt: new Date('2019-11-25T09:00:00.000Z'),
          updatedAt: new Date('2019-11-25T09:00:00.000Z'),
        })
      );
    });

    it('should set title from WXR title', async () => {
      const wxrMedia = createMockWxrMedia({ title: 'Beautiful Sunset' });
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).toHaveBeenCalledWith(
        expect.any(String),
        expect.objectContaining({
          title: 'Beautiful Sunset',
        })
      );
    });

    it('should link media to parent post using wpId mapping', async () => {
      // First import a post
      const wxrPost = createMockWxrPost({ wpId: 42 });
      const wxrMedia = createMockWxrMedia({ parentId: 42 });
      
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).toHaveBeenCalledWith(
        expect.any(String),
        expect.objectContaining({
          linkedPostIds: expect.any(Array),
        })
      );
    });

    it('should skip missing media files', async () => {
      const wxrMedia = createMockWxrMedia();
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 1,
          items: [createMockAnalyzedMedia(wxrMedia, 'missing')],
        },
      });

      const result = await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).not.toHaveBeenCalled();
      expect(result.media.skipped).toBe(1);
    });

    it('should skip media with conflict resolution "ignore"', async () => {
      const wxrMedia = createMockWxrMedia();
      const analyzed = createMockAnalyzedMedia(wxrMedia, 'conflict');
      (analyzed as any).conflictResolution = 'ignore';
      
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          missing: 0,
          items: [analyzed],
        },
      });

      const result = await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).not.toHaveBeenCalled();
      expect(result.media.skipped).toBe(1);
    });

    it('should skip media with status "content-duplicate"', async () => {
      const wxrMedia = createMockWxrMedia();
      const analyzed = createMockAnalyzedMedia(wxrMedia, 'content-duplicate');
      
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 1,
          missing: 0,
          items: [analyzed],
        },
      });

      const result = await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).not.toHaveBeenCalled();
      expect(result.media.skipped).toBe(1);
    });

    it('should skip media with status "update"', async () => {
      const wxrMedia = createMockWxrMedia();
      const analyzed = createMockAnalyzedMedia(wxrMedia, 'update');
      
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 0,
          updates: 1,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [analyzed],
        },
      });

      const result = await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).not.toHaveBeenCalled();
      expect(result.media.skipped).toBe(1);
    });

    it('should skip media when uploadsFolder is not provided', async () => {
      const wxrMedia = createMockWxrMedia();
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      const result = await engine.executeImport(report, {}); // No uploadsFolder

      expect(mockMediaEngine.importMedia).not.toHaveBeenCalled();
      expect(result.media.skipped).toBe(1);
    });

    it('should skip media when source file does not exist', async () => {
      const { access } = await import('fs/promises');
      vi.mocked(access).mockRejectedValueOnce(new Error('ENOENT'));
      
      const wxrMedia = createMockWxrMedia();
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      const result = await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).not.toHaveBeenCalled();
      expect(result.media.skipped).toBe(1);
    });

    it('should import media with conflict resolution "overwrite"', async () => {
      const wxrMedia = createMockWxrMedia();
      const analyzed = createMockAnalyzedMedia(wxrMedia, 'conflict');
      (analyzed as any).conflictResolution = 'overwrite';
      
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          missing: 0,
          items: [analyzed],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).toHaveBeenCalled();
    });

    it('should import media with conflict resolution "import"', async () => {
      const wxrMedia = createMockWxrMedia();
      const analyzed = createMockAnalyzedMedia(wxrMedia, 'conflict');
      (analyzed as any).conflictResolution = 'import';
      
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          missing: 0,
          items: [analyzed],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).toHaveBeenCalled();
    });

    it('should set alt text from WXR description', async () => {
      const wxrMedia = createMockWxrMedia({ description: 'Alt text for image' });
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).toHaveBeenCalledWith(
        expect.any(String),
        expect.objectContaining({
          alt: 'Alt text for image',
        })
      );
    });

    it('should handle media with no parent post', async () => {
      const wxrMedia = createMockWxrMedia({ parentId: 0 });
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(mockMediaEngine.importMedia).toHaveBeenCalledWith(
        expect.any(String),
        expect.objectContaining({
          linkedPostIds: [],
        })
      );
    });

    it('should record error when media import fails', async () => {
      mockMediaEngine.importMedia.mockRejectedValueOnce(new Error('Media import failed'));
      
      const wxrMedia = createMockWxrMedia();
      const report = createMockAnalysisReport({
        media: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          missing: 0,
          items: [createMockAnalyzedMedia(wxrMedia, 'new')],
        },
      });

      const result = await engine.executeImport(report, { uploadsFolder: '/uploads' });

      expect(result.media.errors).toBe(1);
      expect(result.errors.length).toBeGreaterThan(0);
      expect(result.errors[0]).toContain('Failed to import media');
    });
  });

  describe('Phase 4: Page Import', () => {
    it('should import pages with "page" category', async () => {
      const wxrPage = createMockWxrPost({
        postType: 'page',
        categories: [],
      });
      const report = createMockAnalysisReport({
        pages: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPage, 'new')],
        },
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      const categories = JSON.parse(insertedPosts[0].categories);
      expect(categories).toContain('page');
    });

    it('should preserve existing page categories and add "page"', async () => {
      const wxrPage = createMockWxrPost({
        postType: 'page',
        categories: ['Documentation', 'Help'],
      });
      const report = createMockAnalysisReport({
        pages: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPage, 'new')],
        },
        categories: [
          { name: 'Documentation', slug: 'documentation', existsInProject: false },
          { name: 'Help', slug: 'help', existsInProject: false },
        ],
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      const categories = JSON.parse(insertedPosts[0].categories);
      expect(categories).toContain('page');
      expect(categories).toContain('documentation');
      expect(categories).toContain('help');
    });

    it('should use existing page category mapping when defined in report', async () => {
      const wxrPage = createMockWxrPost({
        postType: 'page',
        categories: [],
      });
      const report = createMockAnalysisReport({
        pages: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPage, 'new')],
        },
        categories: [
          // Page is already defined in the category mapping
          { name: 'page', slug: 'page', existsInProject: true },
        ],
      });

      await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(1);
      const categories = JSON.parse(insertedPosts[0].categories);
      expect(categories).toContain('page');
    });

    it('should skip pages with status "content-duplicate"', async () => {
      const wxrPage = createMockWxrPost({
        postType: 'page',
        categories: [],
      });
      const report = createMockAnalysisReport({
        pages: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 1,
          items: [createMockAnalyzedPost(wxrPage, 'content-duplicate')],
        },
      });

      const result = await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(0);
      expect(result.pages.skipped).toBe(1);
    });

    it('should skip pages with status "update"', async () => {
      const wxrPage = createMockWxrPost({
        postType: 'page',
        categories: [],
      });
      const analyzed = createMockAnalyzedPost(wxrPage, 'update');
      analyzed.existingPost = {
        id: 'existing-page-id',
        title: 'Existing Page',
        slug: 'test-page',
        checksum: 'same-content-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      
      const report = createMockAnalysisReport({
        pages: {
          total: 1,
          new: 0,
          updates: 1,
          conflicts: 0,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      const result = await engine.executeImport(report, {});

      expect(insertedPosts.length).toBe(0);
      expect(result.pages.skipped).toBe(1);
    });

    it('should handle page conflict with "overwrite" resolution', async () => {
      const wxrPage = createMockWxrPost({
        postType: 'page',
        categories: [],
      });
      const analyzed = createMockAnalyzedPost(wxrPage, 'conflict', 'overwrite');
      analyzed.existingPost = {
        id: 'existing-page-id',
        title: 'Existing Page',
        slug: 'test-post',
        checksum: 'old-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      
      const report = createMockAnalysisReport({
        pages: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      await engine.executeImport(report, {});

      // Should update existing page, not insert new one
      expect(insertedPosts.length).toBe(0);
      expect(updatedPosts.length).toBeGreaterThan(0);
      expect(updatedPosts[0].data.status).toBe('draft');
      const categories = JSON.parse(updatedPosts[0].data.categories);
      expect(categories).toContain('page');
    });

    it('should handle page conflict with "import" resolution', async () => {
      const wxrPage = createMockWxrPost({
        postType: 'page',
        categories: [],
      });
      const analyzed = createMockAnalyzedPost(wxrPage, 'conflict', 'import');
      analyzed.existingPost = {
        id: 'existing-page-id',
        title: 'Existing Page',
        slug: 'test-post',
        checksum: 'old-checksum',
        pubDate: null,
        excerpt: null,
        author: null,
        tags: [],
        categories: [],
      };
      
      const report = createMockAnalysisReport({
        pages: {
          total: 1,
          new: 0,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [analyzed],
        },
      });

      await engine.executeImport(report, {});

      expect(mockPostEngine.generateUniqueSlug).toHaveBeenCalled();
      expect(insertedPosts.length).toBe(1);
      const categories = JSON.parse(insertedPosts[0].categories);
      expect(categories).toContain('page');
    });

    it('should record error when page import fails', async () => {
      const wxrPage = createMockWxrPost({
        postType: 'page',
        categories: [],
      });
      
      // Make the database insert throw an error
      const originalInsert = mockDb.insert;
      mockDb.insert = vi.fn(() => ({
        values: vi.fn().mockRejectedValue(new Error('Database error')),
      }));
      
      const report = createMockAnalysisReport({
        pages: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPage, 'new')],
        },
      });

      const result = await engine.executeImport(report, {});

      expect(result.pages.errors).toBe(1);
      expect(result.errors.length).toBeGreaterThan(0);
      expect(result.errors[0]).toContain('Failed to import page');
      
      // Restore
      mockDb.insert = originalInsert;
    });
  });

  describe('Progress Reporting', () => {
    it('should call progress callback during import', async () => {
      const progressCallback = vi.fn();
      const wxrPost = createMockWxrPost();
      const report = createMockAnalysisReport({
        posts: {
          total: 1,
          new: 1,
          updates: 0,
          conflicts: 0,
          contentDuplicates: 0,
          items: [createMockAnalyzedPost(wxrPost, 'new')],
        },
      });

      await engine.executeImport(report, { onProgress: progressCallback });

      expect(progressCallback).toHaveBeenCalled();
    });
  });

  describe('Result Summary', () => {
    it('should return accurate counts in result', async () => {
      const report = createMockAnalysisReport({
        tags: [
          { name: 'NewTag', slug: 'newtag', existsInProject: false },
        ],
        posts: {
          total: 2,
          new: 1,
          updates: 0,
          conflicts: 1,
          contentDuplicates: 0,
          items: [
            createMockAnalyzedPost(createMockWxrPost({ wpId: 1 }), 'new'),
            createMockAnalyzedPost(createMockWxrPost({ wpId: 2 }), 'conflict', 'ignore'),
          ],
        },
      });

      const result = await engine.executeImport(report, {});

      expect(result.tags.created).toBe(1);
      expect(result.posts.imported).toBe(1);
      expect(result.posts.skipped).toBe(1);
    });
  });
});
