import { describe, expect, test } from "bun:test";
import { TapOnElement } from "../../../src/features/action/TapOnElement";
import { FakeAdbClient } from "../../fakes/FakeAdbClient";
import { FakeElementSelector } from "../../fakes/FakeElementSelector";
import { FakeTimer } from "../../fakes/FakeTimer";

const createTapOnElement = (selector: FakeElementSelector) => {
  return new TapOnElement(
    {
      name: "test-device",
      platform: "android",
      deviceId: "emulator-5554",
    } as any,
    new FakeAdbClient() as any,
    {
      timer: new FakeTimer(),
      elementSelector: selector,
    }
  );
};

const makeElement = () => ({
  bounds: { left: 0, top: 0, right: 100, bottom: 50 },
  text: "Item",
  clickable: "true",
} as any);

describe("TapOnElement extended selectors", () => {
  describe("validation", () => {
    test("rejects when no selector provided", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);
      const error = (tapOn as any).validateOptions({ action: "tap" });
      expect(error).toContain("requires exactly one");
    });

    test("rejects when both selectors provided", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);
      const error = (tapOn as any).validateOptions({
        action: "tap",
        text: "Login",
        elementId: "com.app:id/btn",
      });
      expect(error).toContain("requires exactly one");
    });

    test("accepts text as sole selector", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);
      const error = (tapOn as any).validateOptions({
        action: "tap",
        text: "Login",
      });
      expect(error).toBeNull();
    });

    test("accepts elementId as sole selector", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);
      const error = (tapOn as any).validateOptions({
        action: "tap",
        elementId: "com.app:id/btn",
      });
      expect(error).toBeNull();
    });

    test("accepts text with sibling flag", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);
      const error = (tapOn as any).validateOptions({
        action: "tap",
        text: "Accept Terms",
        sibling: true,
      });
      expect(error).toBeNull();
    });

    test("accepts elementId with sibling flag", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);
      const error = (tapOn as any).validateOptions({
        action: "tap",
        elementId: "com.app:id/label",
        sibling: true,
      });
      expect(error).toBeNull();
    });
  });

  describe("findElementInHierarchy", () => {
    test("delegates text to selectByText", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);

      const result = (tapOn as any).findElementInHierarchy(
        { text: "Login", action: "tap" },
        { hierarchy: { node: {} } }
      );

      expect(result.selection.element).not.toBeNull();
      expect(selector.lastText).toBe("Login");
    });

    test("delegates elementId to selectByResourceId", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);

      const result = (tapOn as any).findElementInHierarchy(
        { elementId: "com.app:id/btn", action: "tap" },
        { hierarchy: { node: {} } }
      );

      expect(result.selection.element).not.toBeNull();
      expect(selector.lastResourceId).toBe("com.app:id/btn");
    });

    test("text + sibling delegates to selectClickableSiblingOfText", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);

      const result = (tapOn as any).findElementInHierarchy(
        { text: "Accept Terms", sibling: true, action: "tap" },
        { hierarchy: { node: {} } }
      );

      expect(result.selection.element).not.toBeNull();
      expect(selector.lastText).toBe("Accept Terms");
    });

    test("elementId + sibling delegates to selectClickableSiblingOfResourceId", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);

      const result = (tapOn as any).findElementInHierarchy(
        { elementId: "com.app:id/label", sibling: true, action: "tap" },
        { hierarchy: { node: {} } }
      );

      expect(result.selection.element).not.toBeNull();
      expect(selector.lastResourceId).toBe("com.app:id/label");
    });

    test("sibling respects selectionStrategy", () => {
      const selector = new FakeElementSelector(makeElement());
      const tapOn = createTapOnElement(selector);

      (tapOn as any).findElementInHierarchy(
        { text: "Email", sibling: true, action: "tap", selectionStrategy: "random" },
        { hierarchy: { node: {} } }
      );

      expect(selector.lastStrategy).toBe("random");
    });
  });
});
