import { expect, describe, test, beforeEach } from "bun:test";
import { DetectIntentChooser } from "../../../src/features/observe/DetectIntentChooser";
import { BootedDevice, ObserveResult, ViewHierarchyResult } from "../../../src/models";

const testDevice: BootedDevice = { name: "test-device", platform: "android", deviceId: "emulator-5554" };
import { FakeDeepLinkManager } from "../../fakes/FakeDeepLinkManager";
import { FakeObserveScreen } from "../../fakes/FakeObserveScreen";
import { FakeWindow } from "../../fakes/FakeWindow";
import { FakeAwaitIdle } from "../../fakes/FakeAwaitIdle";

describe("DetectIntentChooser", () => {
  let detectIntentChooser: DetectIntentChooser;
  let fakeDeepLinkManager: FakeDeepLinkManager;
  let fakeObserveScreen: FakeObserveScreen;
  let fakeWindow: FakeWindow;
  let fakeAwaitIdle: FakeAwaitIdle;

  const mockObserveResult: ObserveResult = {
    timestamp: "2025-01-01T00:00:00.000Z",
    screenSize: { width: 1080, height: 1920 },
    systemInsets: { top: 0, right: 0, bottom: 0, left: 0 },
    viewHierarchy: {
      hierarchy: {
        node: {
          $: {
            class: "com.android.internal.app.ChooserActivity"
          },
          node: [
            {
              $: {
                text: "Choose an app"
              }
            },
            {
              $: {
                text: "Always",
                class: "android.widget.Button"
              }
            },
            {
              $: {
                text: "Just once",
                class: "android.widget.Button"
              }
            }
          ]
        }
      }
    }
  };

  beforeEach(() => {
    // Create fakes for dependencies
    fakeObserveScreen = new FakeObserveScreen();
    fakeWindow = new FakeWindow();
    fakeAwaitIdle = new FakeAwaitIdle();
    fakeDeepLinkManager = new FakeDeepLinkManager();

    // Configure default responses
    fakeWindow.configureCachedActiveWindow(null);
    fakeWindow.configureActiveWindow({ appId: "com.test.app", activityName: "MainActivity", layoutSeqSum: 123 });
    fakeObserveScreen.setObserveResult(mockObserveResult);
    fakeDeepLinkManager.setDefaultIntentChooserDetected(true);

    // Create DetectIntentChooser instance
    detectIntentChooser = new DetectIntentChooser(testDevice);

    // Replace the internal managers with our fakes
    (detectIntentChooser as any).observeScreen = fakeObserveScreen;
    (detectIntentChooser as any).window = fakeWindow;
    (detectIntentChooser as any).awaitIdle = fakeAwaitIdle;
    (detectIntentChooser as any).deepLinkManager = fakeDeepLinkManager;
  });

  describe("constructor", () => {
    test("should create DetectIntentChooser with device ID", () => {
      const instance = new DetectIntentChooser(testDevice);
      expect(instance).toBeInstanceOf(DetectIntentChooser);
    });
  });

  describe("execute", () => {
    test("should detect intent chooser when provided with view hierarchy", async () => {
      const result = await detectIntentChooser.execute();

      expect(result.success).toBe(true);
      expect(result.detected).toBe(true);
      expect(result.observation).toBe(mockObserveResult);
    });

    test("should not detect intent chooser in normal app view hierarchy", async () => {
      const normalObserveResult = {
        ...mockObserveResult,
        viewHierarchy: {
          hierarchy: {
            node: {
              $: {
                class: "android.widget.LinearLayout"
              },
              node: [
                {
                  $: {
                    text: "Normal app content"
                  }
                }
              ]
            }
          }
        }
      };

      fakeObserveScreen.setObserveResult(normalObserveResult);
      fakeDeepLinkManager.setDefaultIntentChooserDetected(false);

      const result = await detectIntentChooser.execute();

      expect(result.success).toBe(true);
      expect(result.detected).toBe(false);
      expect(result.observation).toBe(normalObserveResult);
    });

    test("should observe screen when no view hierarchy provided", async () => {
      const result = await detectIntentChooser.execute();

      expect(result.success).toBe(true);
      expect(result.detected).toBe(true); // mockObserveResult contains ChooserActivity
      expect(result.observation).toBe(mockObserveResult);
    });

    test("should handle observe screen failure", async () => {
      // Set failure mode for observation
      fakeObserveScreen.setFailureMode("getMostRecentCachedObserveResult", new Error("Cannot perform action without view hierarchy"));
      fakeObserveScreen.setFailureMode("execute", new Error("Cannot perform action without view hierarchy"));

      try {
        await detectIntentChooser.execute();
        expect.fail("Expected an error to be thrown");
      } catch (error) {
        expect((error as Error).message).toContain("Cannot perform action without view hierarchy");
      }
    });

    test("should handle observe screen returning null view hierarchy", async () => {
      // Set failure mode for observation
      fakeObserveScreen.setFailureMode("getMostRecentCachedObserveResult", new Error("Cannot perform action without view hierarchy"));
      fakeObserveScreen.setFailureMode("execute", new Error("Cannot perform action without view hierarchy"));

      try {
        await detectIntentChooser.execute();
        expect.fail("Expected an error to be thrown");
      } catch (error) {
        expect((error as Error).message).toContain("Cannot perform action without view hierarchy");
      }
    });

    test("should handle deep link manager detection failure", async () => {
      // For this test, we need to make detectIntentChooser throw
      // Since FakeDeepLinkManager always succeeds, we test the error handling in the feature
      // by verifying the result when detection is false
      fakeDeepLinkManager.setDefaultIntentChooserDetected(false);

      const result = await detectIntentChooser.execute();

      expect(result.success).toBe(true);
      expect(result.detected).toBe(false);
    });

    test("should detect various intent chooser indicators", async () => {
      const testCases: ViewHierarchyResult[] = [
        {
          hierarchy: {
            node: {
              $: {
                class: "com.android.internal.app.ChooserActivity"
              }
            }
          }
        },
        {
          hierarchy: {
            node: {
              $: {
                class: "com.android.internal.app.ResolverActivity"
              }
            }
          }
        },
        {
          hierarchy: {
            node: {
              $: {
                text: "Choose an app"
              }
            }
          }
        },
        {
          hierarchy: {
            node: {
              $: {
                text: "Open with"
              }
            }
          }
        },
        {
          hierarchy: {
            node: {
              $: {},
              node: [
                {
                  $: {
                    text: "Always"
                  }
                },
                {
                  $: {
                    text: "Just once"
                  }
                }
              ]
            }
          }
        }
      ];

      for (const viewHierarchy of testCases) {
        fakeObserveScreen.setObserveResult({
          ...mockObserveResult,
          viewHierarchy
        });
        fakeDeepLinkManager.setDefaultIntentChooserDetected(true);

        const result = await detectIntentChooser.execute();
        expect(result.success).toBe(true);
        expect(result.detected).toBe(true);
      }
    });

    test("should not detect intent chooser in non-chooser screens", async () => {
      const testCases: ViewHierarchyResult[] = [
        {
          hierarchy: {
            node: {
              $: {
                class: "android.widget.Button",
                text: "Click me"
              }
            }
          }
        },
        {
          hierarchy: {
            node: {
              $: {
                text: "Welcome to the app"
              }
            }
          }
        },
        {
          hierarchy: {
            node: {
              $: {
                class: "com.example.MainActivity"
              }
            }
          }
        }
      ];

      for (const viewHierarchy of testCases) {
        fakeObserveScreen.setObserveResult({
          ...mockObserveResult,
          viewHierarchy
        });
        fakeDeepLinkManager.setDefaultIntentChooserDetected(false);

        const result = await detectIntentChooser.execute();
        expect(result.success).toBe(true);
        expect(result.detected).toBe(false);
      }
    });
  });
});
