import { Page } from '@playwright/test';

/**
 * PM Core Test Helper Functions
 *
 * Shared utilities for PM Core module Playwright tests
 */

/**
 * Login as admin user
 */
export async function loginAsAdmin(page: Page) {
  await page.goto('/auth/login', { waitUntil: 'networkidle' });
  await page.locator('#email').fill('admin@demo.com');
  await page.locator('#password').fill('password123');
  await page.locator('button[type="submit"]').click();
  await page.waitForURL('**/dashboard', { timeout: 15000 });
}

/**
 * Generate unique project name
 */
export function generateUniqueProjectName() {
  return `Test Project ${Date.now().toString().slice(-6)}`;
}

/**
 * Generate unique code with prefix
 */
export function generateUniqueCode(prefix: string = 'PRJ') {
  return prefix + Date.now().toString().slice(-6);
}

/**
 * Generate unique email address
 */
export function generateUniqueEmail() {
  return `test${Date.now()}@example.com`;
}

/**
 * Fill a Select2 dropdown by searching and selecting
 */
export async function fillSelect2(page: Page, selector: string, searchText: string) {
  const select2Container = page.locator(selector).locator('..').locator('.select2-selection');
  await select2Container.click();
  await page.waitForTimeout(300);
  await page.keyboard.type(searchText);
  await page.waitForTimeout(500);
  await page.waitForSelector('.select2-results__option', { state: 'visible', timeout: 5000 });
  const firstOption = page.locator('.select2-results__option').first();
  await firstOption.click();
  await page.waitForTimeout(200);
}

/**
 * Fill Select2 using the sibling approach (for hidden select elements)
 * Improved version with better error handling and dropdown management
 */
export async function fillSelect2ById(page: Page, selectId: string, searchText: string) {
  // Close any open dropdowns first
  await page.keyboard.press('Escape');
  await page.waitForTimeout(200);

  // Click on the Select2 container that follows the hidden select
  const select2Container = page.locator(`#${selectId} + .select2-container`);
  await select2Container.click();
  await page.waitForTimeout(300);

  // Wait for dropdown to open
  await page.waitForSelector('.select2-dropdown', { state: 'visible', timeout: 5000 });

  // Type in the search box
  const searchInput = page.locator('.select2-search__field');
  if (await searchInput.isVisible()) {
    await searchInput.fill(searchText);
  } else {
    await page.keyboard.type(searchText);
  }
  await page.waitForTimeout(500);

  // Wait for results to load (not loading state)
  await page.waitForSelector('.select2-results__option:not(.loading-results):not(.select2-results__option--load-more)', { state: 'visible', timeout: 5000 });

  // Click first result that's not a message
  const firstOption = page.locator('.select2-results__option').filter({ hasNotText: 'Searching' }).first();
  await firstOption.click();
  await page.waitForTimeout(200);
}

/**
 * Set a Flatpickr date input value via JavaScript
 */
export async function setFlatpickrDate(page: Page, selector: string, date: string) {
  await page.evaluate(([sel, val]) => {
    const input = document.querySelector(sel) as HTMLInputElement;
    if (input) {
      // Try to use Flatpickr instance if available
      const fp = (input as any)._flatpickr;
      if (fp) {
        fp.setDate(val, true);
      } else {
        input.value = val;
        input.dispatchEvent(new Event('change', { bubbles: true }));
      }
    }
  }, [selector, date]);
  await page.waitForTimeout(200);
}

/**
 * Get today's date string (YYYY-MM-DD format)
 */
export function getTodayDate(): string {
  return new Date().toISOString().split('T')[0];
}

/**
 * Get future date string (days ahead)
 */
export function getFutureDate(daysAhead: number = 30): string {
  const date = new Date();
  date.setDate(date.getDate() + daysAhead);
  return date.toISOString().split('T')[0];
}

/**
 * Get past date string (days ago)
 */
export function getPastDate(daysAgo: number = 30): string {
  const date = new Date();
  date.setDate(date.getDate() - daysAgo);
  return date.toISOString().split('T')[0];
}

/**
 * Get future date string (months ahead)
 */
export function getFutureDateMonths(monthsAhead: number = 3): string {
  const date = new Date();
  date.setMonth(date.getMonth() + monthsAhead);
  return date.toISOString().split('T')[0];
}

/**
 * Confirm SweetAlert dialog
 */
export async function confirmSweetAlert(page: Page) {
  await page.waitForSelector('.swal2-popup', { state: 'visible', timeout: 5000 });
  await page.click('.swal2-confirm');
  await page.waitForTimeout(500);
}

/**
 * Cancel SweetAlert dialog
 */
export async function cancelSweetAlert(page: Page) {
  await page.waitForSelector('.swal2-popup', { state: 'visible', timeout: 5000 });
  await page.click('.swal2-cancel');
  await page.waitForTimeout(500);
}

/**
 * Wait for success SweetAlert and dismiss it
 */
export async function waitForSuccessSweetAlert(page: Page) {
  await page.waitForSelector('.swal2-popup', { state: 'visible', timeout: 15000 });
  // Wait for success icon or any confirm button
  const okButton = page.locator('.swal2-confirm');
  if (await okButton.isVisible()) {
    await okButton.click();
  }
  await page.waitForTimeout(500);
}

/**
 * Wait for DataTable to reload
 */
export async function waitForDataTableReload(page: Page, tableSelector: string = '.dataTables_wrapper') {
  await page.waitForLoadState('networkidle');
  await page.waitForTimeout(500);
  await page.waitForSelector(tableSelector, { timeout: 10000 });
}

/**
 * Wait for page to be fully loaded and stable
 */
export async function waitForPageStable(page: Page) {
  await page.waitForLoadState('networkidle');
  await page.waitForTimeout(500);
}

/**
 * Check if an element exists on the page
 */
export async function elementExists(page: Page, selector: string): Promise<boolean> {
  return await page.locator(selector).count() > 0;
}

/**
 * Close any open offcanvas
 */
export async function closeOffcanvas(page: Page) {
  const openOffcanvas = page.locator('.offcanvas.show').first();
  if (await openOffcanvas.isVisible()) {
    await page.evaluate(() => {
      const offcanvasElement = document.querySelector('.offcanvas.show');
      if (offcanvasElement) {
        const bsOffcanvas = (window as any).bootstrap?.Offcanvas?.getInstance(offcanvasElement);
        if (bsOffcanvas) {
          bsOffcanvas.hide();
        } else {
          offcanvasElement.classList.remove('show');
          const backdrop = document.querySelector('.offcanvas-backdrop');
          if (backdrop) {
            backdrop.remove();
          }
        }
      }
    });
    await openOffcanvas.waitFor({ state: 'hidden', timeout: 5000 });
    await page.waitForTimeout(300);
  }
}

// ============================================
// PM Core Navigation Helpers
// ============================================

/**
 * Navigate to Projects list page
 */
export async function navigateToProjects(page: Page) {
  await page.goto('/projects', { waitUntil: 'networkidle' });
  await page.waitForSelector('.card', { timeout: 10000 });
}

/**
 * Navigate to Project create page
 */
export async function navigateToProjectCreate(page: Page) {
  await page.goto('/projects/create', { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Project show page
 */
export async function navigateToProjectShow(page: Page, projectId: number) {
  await page.goto(`/projects/${projectId}`, { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Project edit page
 */
export async function navigateToProjectEdit(page: Page, projectId: number) {
  await page.goto(`/projects/${projectId}/edit`, { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Project Dashboard
 */
export async function navigateToProjectDashboard(page: Page) {
  await page.goto('/projects/dashboard', { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Timesheets list page
 */
export async function navigateToTimesheets(page: Page) {
  await page.goto('/projects/timesheets', { waitUntil: 'networkidle' });
  await page.waitForSelector('.card', { timeout: 10000 });
}

/**
 * Navigate to Timesheet create page
 */
export async function navigateToTimesheetCreate(page: Page) {
  await page.goto('/projects/timesheets/create', { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Timesheet edit page
 */
export async function navigateToTimesheetEdit(page: Page, timesheetId: number) {
  await page.goto(`/projects/timesheets/${timesheetId}/edit`, { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Resources page
 */
export async function navigateToResources(page: Page) {
  await page.goto('/projects/resources', { waitUntil: 'networkidle' });
  await page.waitForSelector('.card', { timeout: 10000 });
}

/**
 * Navigate to Resource Capacity page
 */
export async function navigateToResourceCapacity(page: Page) {
  await page.goto('/projects/resources/capacity', { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Project Statuses page
 */
export async function navigateToProjectStatuses(page: Page) {
  await page.goto('/projects/project-statuses', { waitUntil: 'networkidle' });
  // Wait for either card or table to be visible
  await page.waitForSelector('.card, table, .datatables-project-statuses', { timeout: 15000 });
}

/**
 * Navigate to Reports page
 */
export async function navigateToReports(page: Page) {
  await page.goto('/projects/reports', { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Time Report page
 */
export async function navigateToTimeReport(page: Page) {
  await page.goto('/projects/reports/time', { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Budget Report page
 */
export async function navigateToBudgetReport(page: Page) {
  await page.goto('/projects/reports/budget', { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Resource Report page
 */
export async function navigateToResourceReport(page: Page) {
  await page.goto('/projects/reports/resource', { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Project Tasks page
 */
export async function navigateToProjectTasks(page: Page, projectId: number) {
  await page.goto(`/projects/${projectId}/tasks`, { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

/**
 * Navigate to Project Task Board page
 */
export async function navigateToProjectTaskBoard(page: Page, projectId: number) {
  await page.goto(`/projects/${projectId}/tasks/board`, { waitUntil: 'networkidle' });
  await waitForPageStable(page);
}

// ============================================
// Project CRUD Helpers
// ============================================

export interface ProjectData {
  name: string;
  code?: string;
  description?: string;
  client?: string;
  projectManager?: string;
  type?: string;
  status?: string;
  priority?: string;
  startDate?: string;
  endDate?: string;
  budget?: number;
  hourlyRate?: number;
  isBillable?: boolean;
}

/**
 * Fill project form with data
 */
export async function fillProjectForm(page: Page, data: ProjectData) {
  // Basic Information
  await page.locator('#name').fill(data.name);

  if (data.code) {
    await page.locator('#code').fill(data.code);
  }

  if (data.description) {
    await page.locator('#description').fill(data.description);
  }

  // Project Details
  if (data.client) {
    await fillSelect2ById(page, 'client_id', data.client);
  }

  if (data.projectManager) {
    await fillSelect2ById(page, 'project_manager_id', data.projectManager);
  }

  if (data.type) {
    await page.selectOption('#type', data.type);
  }

  if (data.status) {
    await page.selectOption('#status', data.status);
  }

  if (data.priority) {
    await page.selectOption('#priority', data.priority);
  }

  // Schedule & Budget
  if (data.startDate) {
    await setFlatpickrDate(page, '#start_date', data.startDate);
  }

  if (data.endDate) {
    await setFlatpickrDate(page, '#end_date', data.endDate);
  }

  if (data.budget !== undefined) {
    await page.locator('#budget').fill(data.budget.toString());
  }

  if (data.hourlyRate !== undefined) {
    await page.locator('#hourly_rate').fill(data.hourlyRate.toString());
  }

  if (data.isBillable !== undefined) {
    const checkbox = page.locator('#is_billable');
    const isChecked = await checkbox.isChecked();
    if (isChecked !== data.isBillable) {
      await checkbox.click();
    }
  }
}

// ============================================
// Timesheet Helpers
// ============================================

export interface TimesheetData {
  userId?: number;
  projectName: string;
  taskName?: string;
  date: string;
  hours: number;
  description?: string;
  isBillable?: boolean;
  billingRate?: number;
  costRate?: number;
}

/**
 * Fill timesheet form with data
 */
export async function fillTimesheetForm(page: Page, data: TimesheetData) {
  // Set date
  await setFlatpickrDate(page, '#date', data.date);

  // Select project using Select2 AJAX
  await fillSelect2ById(page, 'project_id', data.projectName);

  // Wait for tasks to load if project has tasks
  await page.waitForTimeout(500);

  // Select task if provided
  if (data.taskName) {
    await fillSelect2ById(page, 'task_id', data.taskName);
  }

  // Enter hours
  await page.locator('#hours').fill(data.hours.toString());

  // Set billable checkbox
  if (data.isBillable !== undefined) {
    const checkbox = page.locator('#is_billable');
    const isChecked = await checkbox.isChecked();
    if (isChecked !== data.isBillable) {
      await checkbox.click();
    }
  }

  // Enter description if provided
  if (data.description) {
    await page.locator('#description').fill(data.description);
  }

  // Enter billing rate if provided
  if (data.billingRate !== undefined) {
    await page.locator('#billing_rate').fill(data.billingRate.toString());
  }

  // Enter cost rate if provided
  if (data.costRate !== undefined) {
    await page.locator('#cost_rate').fill(data.costRate.toString());
  }
}

// ============================================
// DataTable Action Helpers
// ============================================

/**
 * Click action button in DataTable row
 * Handles responsive DataTables where actions may be hidden
 */
export async function clickTableAction(page: Page, rowIndex: number, actionName: string) {
  const rows = page.locator('table tbody tr:not(.child)');
  const row = rows.nth(rowIndex);

  // Check if table is responsive (has expand control)
  const expandControl = row.locator('.dtr-control, td.dtr-control').first();
  if (await expandControl.count() > 0 && await expandControl.isVisible()) {
    await expandControl.click();
    await page.waitForTimeout(300);
  }

  // Try to find dropdown toggle in main row or expanded child row
  let actionDropdown = row.locator('.dropdown-toggle, button.btn-icon').first();

  // If not visible in main row, check in child row (responsive expanded content)
  if (!await actionDropdown.isVisible()) {
    const childRow = page.locator('table tbody tr.child').first();
    if (await childRow.count() > 0) {
      actionDropdown = childRow.locator('.dropdown-toggle, button.btn-icon').first();
    }
  }

  await actionDropdown.click({ force: true });
  await page.waitForTimeout(300);

  const actionButton = page.locator('.dropdown-menu.show').locator(`.dropdown-item:has-text("${actionName}")`).first();
  await actionButton.click();
  await page.waitForTimeout(300);
}

/**
 * Search in DataTable
 */
export async function searchInDataTable(page: Page, searchText: string) {
  const searchInput = page.locator('.dataTables_filter input[type="search"]');
  await searchInput.fill(searchText);
  await page.waitForTimeout(1000); // Wait for AJAX filter
}

/**
 * Get row count in DataTable
 */
export async function getDataTableRowCount(page: Page): Promise<number> {
  return await page.locator('table tbody tr').count();
}

/**
 * Wait for DataTable to initialize
 */
export async function waitForDataTable(page: Page) {
  await page.waitForSelector('.dataTables_wrapper', { timeout: 10000 });
  await page.waitForTimeout(500);
}
