import { faker } from "@faker-js/faker";
import {
  ElementBounds,
  Element,
  ScreenSize,
  SystemInsets,
  Point,
  ObserveResult,
  ActiveWindowInfo,
  TapResult,
  TapOnElementResult,
  TapOnSelectedElement,
  ExitDialogResult,
  LaunchAppResult,
  TerminateAppResult,
  ClearAppDataResult,
  InstallAppResult,
  OpenURLResult,
  SendTextResult,
  SendKeyEventResult,
  PressButtonResult,
  RotateResult,
  SwipeResult
} from "../../src/models";

/**
 * Utility class for generating fake model data for testing
 */
export class ResultFaker {
  /**
   * Set a seed for deterministic fake data generation
   */
  static setSeed(seed: number): void {
    faker.seed(seed);
  }

  /**
   * Generate fake ElementBounds
   */
  static elementBounds(overrides: Partial<ElementBounds> = {}): ElementBounds {
    return {
      left: overrides.left ?? faker.number.int({ min: 0, max: 1000 }),
      top: overrides.top ?? faker.number.int({ min: 0, max: 1600 }),
      right: overrides.right ?? faker.number.int({ min: overrides.left ?? 0, max: 1080 }),
      bottom: overrides.bottom ?? faker.number.int({ min: overrides.top ?? 0, max: 1920 })
    };
  }

  /**
   * Generate fake Element
   */
  static element(overrides: Partial<Element> = {}): Element {
    const bounds = overrides.bounds ?? this.elementBounds();

    return {
      bounds,
      "text": overrides.text ?? faker.helpers.maybe(() => faker.lorem.words({ min: 1, max: 5 })),
      "content-desc": overrides["content-desc"] ?? faker.helpers.maybe(() => faker.lorem.sentence()),
      "resource-id": overrides["resource-id"] ?? faker.helpers.maybe(() => `${faker.word.sample()}:id/${faker.word.sample()}_${faker.word.sample()}`),
      "class": overrides["class"] ?? faker.helpers.arrayElement(["android.widget.TextView", "android.widget.Button", "android.widget.ImageView"]),
      "package": overrides["package"] ?? `com.${faker.internet.domainWord()}.${faker.internet.domainWord()}`,
      "checkable": overrides.checkable ?? faker.datatype.boolean(),
      "checked": overrides.checked ?? faker.datatype.boolean(),
      "clickable": overrides.clickable ?? faker.datatype.boolean(0.7),
      "enabled": overrides.enabled ?? faker.datatype.boolean(0.9),
      "focusable": overrides.focusable ?? faker.datatype.boolean(0.5),
      "focused": overrides.focused ?? faker.datatype.boolean(0.2),
      "scrollable": overrides.scrollable ?? faker.datatype.boolean(0.3),
      "selected": overrides.selected ?? faker.datatype.boolean(0.3),
      ...overrides
    };
  }

  /**
   * Generate fake TapOnSelectedElement metadata
   */
  static tapOnSelectedElement(
    element: Element,
    overrides: Partial<TapOnSelectedElement> = {}
  ): TapOnSelectedElement {
    const bounds = element.bounds;
    const centerX = Math.floor((bounds.left + bounds.right) / 2);
    const centerY = Math.floor((bounds.top + bounds.bottom) / 2);
    const text = typeof element.text === "string" && element.text.length > 0
      ? element.text
      : (typeof element["content-desc"] === "string"
        ? element["content-desc"]
        : (typeof element["ios-accessibility-label"] === "string"
          ? element["ios-accessibility-label"]
          : ""));

    return {
      text: overrides.text ?? text,
      resourceId: overrides.resourceId ?? (typeof element["resource-id"] === "string"
        ? element["resource-id"]
        : ""),
      bounds: overrides.bounds ?? {
        left: bounds.left,
        top: bounds.top,
        right: bounds.right,
        bottom: bounds.bottom,
        centerX,
        centerY
      },
      indexInMatches: overrides.indexInMatches ?? 0,
      totalMatches: overrides.totalMatches ?? 1,
      selectionStrategy: overrides.selectionStrategy ?? "first"
    };
  }

  /**
   * Generate fake ScreenSize
   */
  static screenSize(overrides: Partial<ScreenSize> = {}): ScreenSize {
    return {
      width: overrides.width ?? faker.helpers.arrayElement([720, 1080, 1440]),
      height: overrides.height ?? faker.helpers.arrayElement([1280, 1920, 2560])
    };
  }

  /**
   * Generate fake SystemInsets
   */
  static systemInsets(overrides: Partial<SystemInsets> = {}): SystemInsets {
    return {
      top: overrides.top ?? faker.number.int({ min: 24, max: 80 }),
      right: overrides.right ?? faker.number.int({ min: 0, max: 48 }),
      bottom: overrides.bottom ?? faker.number.int({ min: 48, max: 144 }),
      left: overrides.left ?? faker.number.int({ min: 0, max: 48 })
    };
  }

  /**
   * Generate fake Point
   */
  static point(overrides: Partial<Point> = {}): Point {
    return {
      x: overrides.x ?? faker.number.int({ min: 0, max: 1080 }),
      y: overrides.y ?? faker.number.int({ min: 0, max: 1920 }),
      delay: overrides.delay ?? faker.helpers.maybe(() => faker.number.int({ min: 10, max: 500 }))
    };
  }

  /**
   * Generate fake ActiveWindowInfo
   */
  static activeWindowInfo(overrides: Partial<ActiveWindowInfo> = {}): ActiveWindowInfo {
    const packageName = overrides.appId ?? `com.${faker.internet.domainWord()}.${faker.internet.domainWord()}`;
    const word = faker.word.sample();
    const capitalizedWord = word.charAt(0).toUpperCase() + word.slice(1);

    return {
      appId: packageName,
      activityName: overrides.activityName ?? `${packageName}.activities.${capitalizedWord}Activity`,
      layoutSeqSum: overrides.layoutSeqSum ?? faker.number.int({ min: 100, max: 10000 })
    };
  }

  /**
   * Generate fake ObserveResult
   */
  static observeResult(overrides: Partial<ObserveResult> = {}): ObserveResult {
    const screenSize = overrides.screenSize ?? this.screenSize();
    const systemInsets = overrides.systemInsets ?? this.systemInsets();
    const updatedAt = overrides.updatedAt ?? Date.now();

    // Create some fake elements
    const elementCount = faker.number.int({ min: 5, max: 20 });
    const elements = Array.from({ length: elementCount }, () => this.element());

    return {
      updatedAt,
      screenSize,
      systemInsets,
      viewHierarchy: overrides.viewHierarchy ?? {
        version: "1.0",
        nodes: elements.map(e => ({ ...e, children: [] }))
      },
      screenshotPath: overrides.screenshotPath ?? faker.helpers.maybe(() => `screenshots/screen_${updatedAt}.png`),
      activeWindow: overrides.activeWindow ?? faker.helpers.maybe(() => this.activeWindowInfo()),
      elements: overrides.elements ?? {
        clickable: elements.filter(e => e.clickable),
        scrollable: elements.filter(e => e.scrollable),
        text: elements.filter(e => e.text)
      },
      error: overrides.error
    };
  }

  /**
   * Generate fake TapResult
   */
  static tapResult(overrides: Partial<TapResult> = {}): TapResult {
    const x = overrides.x ?? faker.number.int({ min: 0, max: 1080 });
    const y = overrides.y ?? faker.number.int({ min: 0, max: 1920 });

    return {
      success: overrides.success ?? true,
      x,
      y,
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error
    };
  }

  /**
   * Generate fake TapOnTextResult
   */
  static tapOnTextResult(overrides: Partial<TapOnElementResult> = {}): TapOnElementResult {
    const element = overrides.element ?? this.element();
    const selectedElement = overrides.selectedElement ?? this.tapOnSelectedElement(element);

    return {
      success: overrides.success ?? true,
      action: overrides.action ?? "tap",
      element,
      selectedElement,
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error
    };
  }

  /**
   * Generate fake ExitDialogResult
   */
  static exitDialogResult(overrides: Partial<ExitDialogResult> = {}): ExitDialogResult {
    const success = overrides.success ?? faker.datatype.boolean(0.9);
    const elementFound = overrides.elementFound ?? success;

    return {
      success,
      elementFound,
      element: elementFound ? (overrides.element ?? this.element({
        text: faker.helpers.arrayElement(["Close", "Cancel", "X", "Exit", "No thanks"]),
        clickable: true
      })) : undefined,
      x: elementFound ? (overrides.x ?? faker.number.int({ min: 0, max: 1080 })) : undefined,
      y: elementFound ? (overrides.y ?? faker.number.int({ min: 0, max: 1920 })) : undefined,
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error ?? (!success ? "Failed to exit dialog" : undefined)
    };
  }

  /**
   * Generate fake LaunchAppResult
   */
  static launchAppResult(overrides: Partial<LaunchAppResult> = {}): LaunchAppResult {
    const packageName = overrides.packageName ?? `com.${faker.internet.domainWord()}.${faker.internet.domainWord()}`;

    return {
      success: overrides.success ?? true,
      packageName,
      activityName: overrides.activityName ?? `${packageName}.MainActivity`,
      observation: overrides.observation ?? this.observeResult({
        activeWindow: this.activeWindowInfo({ appId: packageName })
      }),
      error: overrides.error
    };
  }

  /**
   * Generate fake TerminateAppResult
   */
  static terminateAppResult(overrides: Partial<TerminateAppResult> = {}): TerminateAppResult {
    const packageName = overrides.packageName ?? `com.${faker.internet.domainWord()}.${faker.internet.domainWord()}`;

    return {
      success: overrides.success ?? true,
      packageName,
      wasInstalled: overrides.wasInstalled ?? true,
      wasRunning: overrides.wasRunning ?? faker.datatype.boolean(0.7),
      wasForeground: overrides.wasForeground ?? faker.datatype.boolean(0.5),
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error
    };
  }

  /**
   * Generate fake ClearAppDataResult
   */
  static clearAppDataResult(overrides: Partial<ClearAppDataResult> = {}): ClearAppDataResult {
    const packageName = overrides.packageName ?? `com.${faker.internet.domainWord()}.${faker.internet.domainWord()}`;

    return {
      success: overrides.success ?? true,
      packageName,
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error
    };
  }

  /**
   * Generate fake InstallAppResult
   */
  static installAppResult(overrides: Partial<InstallAppResult> = {}): InstallAppResult {
    return {
      success: overrides.success ?? true,
      artifactPath: overrides.artifactPath ?? `/path/to/${faker.system.fileName({ extensionCount: 0 })}.apk`,
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error
    };
  }

  /**
   * Generate fake OpenURLResult
   */
  static openURLResult(overrides: Partial<OpenURLResult> = {}): OpenURLResult {
    return {
      success: overrides.success ?? true,
      url: overrides.url ?? faker.internet.url(),
      observation: overrides.observation ?? this.observeResult({
        activeWindow: this.activeWindowInfo({ appId: "com.android.chrome" })
      }),
      error: overrides.error
    };
  }

  /**
   * Generate fake SendTextResult
   */
  static sendTextResult(overrides: Partial<SendTextResult> = {}): SendTextResult {
    return {
      success: overrides.success ?? true,
      text: overrides.text ?? faker.lorem.sentence(),
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error
    };
  }

  /**
   * Generate fake SendKeyEventResult
   */
  static sendKeyEventResult(overrides: Partial<SendKeyEventResult> = {}): SendKeyEventResult {
    return {
      success: overrides.success ?? true,
      keyCode: overrides.keyCode ?? faker.number.int({ min: 0, max: 280 }),
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error
    };
  }

  /**
   * Generate fake PressButtonResult
   */
  static pressButtonResult(overrides: Partial<PressButtonResult> = {}): PressButtonResult {
    const button = overrides.button ?? faker.helpers.arrayElement(["home", "back", "menu", "power", "volume_up", "volume_down"]);

    // Map button names to keycodes
    const keyCodeMap: Record<string, number> = {
      home: 3,
      back: 4,
      menu: 82,
      power: 26,
      volume_up: 24,
      volume_down: 25
    };

    return {
      success: overrides.success ?? true,
      button,
      keyCode: overrides.keyCode ?? keyCodeMap[button] ?? 0,
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error
    };
  }

  /**
   * Generate fake RotateResult
   */
  static rotateResult(overrides: Partial<RotateResult> = {}): RotateResult {
    const orientation = overrides.orientation ?? faker.helpers.arrayElement(["portrait", "landscape"]);

    return {
      success: overrides.success ?? true,
      orientation,
      value: overrides.value ?? (orientation === "portrait" ? 0 : 1),
      observation: overrides.orientation === "portrait"
        ? this.observeResult({ screenSize: { width: 1080, height: 1920 } })
        : this.observeResult({ screenSize: { width: 1920, height: 1080 } }),
      error: overrides.error
    };
  }

  /**
   * Generate fake SwipeResult
   */
  static swipeResult(overrides: Partial<SwipeResult> = {}): SwipeResult {
    const x1 = overrides.x1 ?? faker.number.int({ min: 0, max: 1080 });
    const y1 = overrides.y1 ?? faker.number.int({ min: 0, max: 1920 });
    const x2 = overrides.x2 ?? faker.number.int({ min: 0, max: 1080 });
    const y2 = overrides.y2 ?? faker.number.int({ min: 0, max: 1920 });

    return {
      success: overrides.success ?? true,
      x1,
      y1,
      x2,
      y2,
      duration: overrides.duration ?? faker.number.int({ min: 200, max: 800 }),
      path: overrides.path,
      easing: overrides.easing ?? faker.helpers.arrayElement(["linear", "decelerate", "accelerate", "accelerateDecelerate"]),
      observation: overrides.observation ?? this.observeResult(),
      error: overrides.error
    };
  }
}
