import type { Locator, Page } from "playwright";

function chainPartLocator(parent: Locator, part: string): Locator {
  const nthMatch = part.match(/^nth=(\d+)$/);
  if (nthMatch) {
    return parent.nth(Number(nthMatch[1]));
  }
  if (part === "first") {
    return parent.first();
  }

  const roleMatch = part.match(/^role=(\w+)(?:\[name="([^"]*)"\])?$/);
  if (roleMatch) {
    const [, role, name] = roleMatch;
    return name
      ? parent.getByRole(role as Parameters<Locator["getByRole"]>[0], { name })
      : parent.getByRole(role as Parameters<Locator["getByRole"]>[0]);
  }
  if (part.startsWith("text=")) {
    const text = part.slice(5);
    if (text.startsWith("/") && text.endsWith("/")) {
      return parent.getByText(new RegExp(text.slice(1, -1)));
    }
    return parent.getByText(text);
  }
  if (part.startsWith("placeholder=")) {
    const ph = part.slice(12).replace(/^"|"$/g, "");
    return parent.getByPlaceholder(ph);
  }
  if (part.startsWith("filter:text=")) {
    const textPart = part.slice(12);
    if (textPart.startsWith("/") && textPart.endsWith("/")) {
      return parent.locator("div").filter({ hasText: new RegExp(textPart.slice(1, -1)) });
    }
    return parent.locator("div").filter({ hasText: textPart });
  }
  return parent.locator(part);
}

function applyChain(locator: Locator, chain: string): Locator {
  const parts = chain.split(">>").map((p) => p.trim()).filter(Boolean);
  let current = locator;
  for (const part of parts) {
    current = chainPartLocator(current, part);
  }
  return current;
}

/**
 * 将 .env 中的 selector 规格解析为 Playwright Locator。
 * 支持：CSS、`role=`、`text=`、`placeholder=`、`filter:text=`，以及 `>>nth=N` 链。
 */
export function pageLocator(page: Page, spec: string): Locator {
  const [baseSpec, ...chainParts] = spec.split(">>").map((p) => p.trim());
  const trimmed = baseSpec ?? "";

  let base: Locator;

  const roleMatch = trimmed.match(/^role=(\w+)(?:\[name="([^"]*)"\])?$/);
  if (roleMatch) {
    const [, role, name] = roleMatch;
    base = name
      ? page.getByRole(role as Parameters<Page["getByRole"]>[0], { name })
      : page.getByRole(role as Parameters<Page["getByRole"]>[0]);
  } else if (trimmed.startsWith("text=")) {
    const text = trimmed.slice(5);
    if (text.startsWith("/") && text.endsWith("/")) {
      base = page.getByText(new RegExp(text.slice(1, -1)));
    } else {
      base = page.getByText(text);
    }
  } else if (trimmed.startsWith("placeholder=")) {
    const ph = trimmed.slice(12).replace(/^"|"$/g, "");
    base = page.getByPlaceholder(ph);
  } else if (trimmed.startsWith("filter:text=")) {
    const textPart = trimmed.slice(12);
    if (textPart.startsWith("/") && textPart.endsWith("/")) {
      base = page.locator("div").filter({ hasText: new RegExp(textPart.slice(1, -1)) });
    } else {
      base = page.locator("div").filter({ hasText: textPart });
    }
  } else {
    base = page.locator(trimmed);
  }

  if (chainParts.length === 0) {
    return base;
  }
  return applyChain(base, chainParts.join(" >> "));
}

/** 取 spec 匹配到的第一个可见元素 */
export async function firstVisibleLocator(
  page: Page,
  spec: string,
): Promise<Locator | null> {
  const root = pageLocator(page, spec);
  const count = await root.count().catch(() => 0);
  for (let i = 0; i < count; i++) {
    const item = root.nth(i);
    if (await item.isVisible().catch(() => false)) {
      return item;
    }
  }
  return null;
}
