import { renderHook, screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { ApplicationDetails } from "../pages/ApplicationDetails"
import { renderWithProvidersAndRoute } from "./test-utils"
import { onboardingService } from "@/services/onboarding.service"
import { SHARED_APP_STATUSES } from "@/testing/constants"
import { createGlobalMockApplication, mockOnboardingService } from "@/testing/mocks"
import { useApplicationDetailsPermissions } from "../hooks/useApplicationDetailsPermissions"
import { useApplicationDetailsStageData } from "../hooks/useApplicationDetailsStageData"

describe("useApplicationDetailsPermissions — role-based action eligibility", () => {
  const verifiedDeposit = (type: string) =>
    ({ FIRST_FILL: { status: "Verified" }, SECURITY_DEPOSIT: { status: "Verified" } } as Record<string, { status: string }>)[type]

  const baseApp = (status: string, extras: Record<string, unknown> = {}) =>
    ({
      id: "app-1",
      status,
      stageApprovals: [],
      ...extras,
    }) as any

  const user = (role: string, id = "u1") => ({ id, role, name: "Tester" }) as any

  it("allows DD Admin to approve during LOA Pending when deposits are verified and no decision exists", () => {
    const { result } = renderHook(() =>
      useApplicationDetailsPermissions({
        application: baseApp("LOA Pending"),
        interviews: [],
        currentUser: user("DD Admin"),
        getDeposit: verifiedDeposit,
        eorProgress: 0,
      })
    )
    expect(result.current.permissions.canApprove).toBe(true)
    expect(result.current.permissions.canReject).toBe(true)
    expect(result.current.permissions.canAssign).toBe(true)
  })

  it("denies FDD role approve/reject on LOA Pending (approval sequence not met)", () => {
    const { result } = renderHook(() =>
      useApplicationDetailsPermissions({
        application: baseApp("LOA Pending"),
        interviews: [],
        currentUser: user("FDD"),
        getDeposit: verifiedDeposit,
        eorProgress: 0,
      })
    )
    expect(result.current.permissions.canApprove).toBe(false)
    expect(result.current.permissions.canReject).toBe(false)
  })

  it("blocks approve when LOA is payment-locked (first fill not verified)", () => {
    const pendingFirstFill = (type: string) =>
      type === "FIRST_FILL"
        ? { status: "Pending" }
        : { status: "Verified" }

    const { result } = renderHook(() =>
      useApplicationDetailsPermissions({
        application: baseApp("LOA Pending"),
        interviews: [],
        currentUser: user("DD Admin"),
        getDeposit: pendingFirstFill,
        eorProgress: 0,
      })
    )
    expect(result.current.permissions.isLoaLocked).toBe(true)
    expect(result.current.permissions.canApprove).toBe(false)
    expect(result.current.permissions.canReject).toBe(false)
  })

  it("requires DD Head (or NBH after DD Head) sequence on LOI In Progress", () => {
    const loiApp = baseApp("LOI In Progress")

    const nbhBeforeHead = renderHook(() =>
      useApplicationDetailsPermissions({
        application: loiApp,
        interviews: [],
        currentUser: user("NBH"),
        getDeposit: verifiedDeposit,
        eorProgress: 0,
      })
    )
    expect(nbhBeforeHead.result.current.permissions.canApprove).toBe(false)

    const loiWithHeadApproval = baseApp("LOI In Progress", {
      stageApprovals: [
        { stageCode: "LOI_APPROVAL", actorRole: "DD Head", decision: "Approved", actorUserId: "dh-1" },
      ],
    })
    const nbhAfterHead = renderHook(() =>
      useApplicationDetailsPermissions({
        application: loiWithHeadApproval,
        interviews: [],
        currentUser: user("NBH"),
        getDeposit: verifiedDeposit,
        eorProgress: 0,
      })
    )
    expect(nbhAfterHead.result.current.permissions.canApprove).toBe(true)

    const ddHead = renderHook(() =>
      useApplicationDetailsPermissions({
        application: loiApp,
        interviews: [],
        currentUser: user("DD Head"),
        getDeposit: verifiedDeposit,
        eorProgress: 0,
      })
    )
    expect(ddHead.result.current.permissions.canApprove).toBe(true)
  })

  it("exposes canSchedule for DD AM / ASM when interviews are incomplete, not for RBM", () => {
    const early = baseApp("Shortlisted")
    const interviews: any[] = []

    expect(
      renderHook(() =>
        useApplicationDetailsPermissions({
          application: early,
          interviews,
          currentUser: user("DD AM"),
          getDeposit: verifiedDeposit,
          eorProgress: 0,
        })
      ).result.current.permissions.canSchedule
    ).toBe(true)

    expect(
      renderHook(() =>
        useApplicationDetailsPermissions({
          application: early,
          interviews,
          currentUser: user("ASM"),
          getDeposit: verifiedDeposit,
          eorProgress: 0,
        })
      ).result.current.permissions.canSchedule
    ).toBe(true)

    expect(
      renderHook(() =>
        useApplicationDetailsPermissions({
          application: early,
          interviews,
          currentUser: user("RBM"),
          getDeposit: verifiedDeposit,
          eorProgress: 0,
        })
      ).result.current.permissions.canSchedule
    ).toBe(false)
  })

  it("allows Assign User only for DD Admin, Super Admin, DD AM", () => {
    const app = baseApp("Shortlisted")

    expect(
      renderHook(() =>
        useApplicationDetailsPermissions({
          application: app,
          interviews: [],
          currentUser: user("DD Admin"),
          getDeposit: verifiedDeposit,
          eorProgress: 0,
        })
      ).result.current.permissions.canAssign
    ).toBe(true)

    expect(
      renderHook(() =>
        useApplicationDetailsPermissions({
          application: app,
          interviews: [],
          currentUser: user("ASM"),
          getDeposit: verifiedDeposit,
          eorProgress: 0,
        })
      ).result.current.permissions.canAssign
    ).toBe(false)
  })
})

describe("useApplicationDetailsStageData — progress track transitions", () => {
  const verifiedDeposit = (type: string) =>
    ({ FIRST_FILL: { status: "Verified" }, SECURITY_DEPOSIT: { status: "Verified" } } as Record<string, { status: string }>)[type]

  const stagesOf = (application: any, interviews: any[] = [], documents: any[] = []) =>
    renderHook(() =>
      useApplicationDetailsStageData({
        application,
        documents,
        interviews,
        eorData: {},
        getDeposit: verifiedDeposit,
      })
    ).result.current.processStages

  it("marks Questionnaire as active when status is Questionnaire Pending", () => {
    const stages = stagesOf({ status: "Questionnaire Pending", progressTracking: [] })
    const q = stages.find((s) => s.name === "Questionnaire")
    expect(q?.status).toBe("active")
    const sub = stages.find((s) => s.name === "Submitted")
    expect(sub?.status).toBe("completed")
  })

  it("transitions first interview to active when Level 1 is pending and L1 is scheduled", () => {
    const stages = stagesOf(
      { status: "Level 1 Interview Pending", progressTracking: [] },
      [{ level: 1, status: "Scheduled" }]
    )
    const first = stages.find((s) => s.name === "1st Level Interview")
    expect(first?.status).toBe("active")
  })

  it("marks Level 1 completed after Level 1 Approved", () => {
    const stages = stagesOf({ status: "Level 1 Approved", progressTracking: [] })
    expect(stages.find((s) => s.name === "1st Level Interview")?.status).toBe("completed")
    expect(stages.find((s) => s.name === "2nd Level Interview")?.status).toBe("pending")
  })

  it("activates FDD stage when status is FDD Verification", () => {
    const stages = stagesOf({ status: "FDD Verification", progressTracking: [] })
    expect(stages.find((s) => s.name === "FDD")?.status).toBe("active")
    expect(stages.find((s) => s.name === "3rd Level Interview")?.status).toBe("completed")
  })

  it("completes Dealership Active only when onboarded", () => {
    const before = stagesOf({ status: "Inauguration", progressTracking: [] })
    expect(before.find((s) => s.name === "Dealership Active")?.status).toBe("active")

    const after = stagesOf({ status: "Onboarded", progressTracking: [] })
    expect(after.find((s) => s.name === "Dealership Active")?.status).toBe("completed")
  })

  it("respects progressTracking from API when stage is active or completed", () => {
    const stages = stagesOf({
      status: "Shortlisted",
      progressTracking: [{ stageName: "Questionnaire", status: "active" }],
    })
    expect(stages.find((s) => s.name === "Questionnaire")?.status).toBe("active")
  })

  it("sets LOA stage locked when first fill is not verified", () => {
    const getDep = (type: string) =>
      (type === "FIRST_FILL" ? { status: "Pending" } : { status: "Verified" }) as { status: string }

    const { result } = renderHook(() =>
      useApplicationDetailsStageData({
        application: { status: "LOA Pending", progressTracking: [] },
        documents: [],
        interviews: [],
        eorData: {},
        getDeposit: getDep,
      })
    )
    const loa = result.current.processStages.find((s) => s.name === "LOA")
    expect(loa?.isLocked).toBe(true)
    expect(loa?.status).toBe("active")
  })
})

describe("ApplicationDetails UI — schedule button visibility by role", () => {
  beforeEach(() => {
    jest.clearAllMocks()
  })

  it("shows Schedule Interview for DD AM when pipeline allows scheduling", async () => {
    const appData = createGlobalMockApplication({
      overallStatus: "Shortlisted",
    })
    ;(mockOnboardingService.getApplicationById as jest.Mock).mockResolvedValue(appData)

    renderWithProvidersAndRoute(<ApplicationDetails />, {
      initialEntry: `/applications/${appData.id}`,
      routePath: "/applications/:id",
      preloadedState: {
        auth: {
          user: { id: "am-1", name: "AM", role: "DD AM" } as any,
          token: "t",
          isAuthenticated: true,
          loading: false,
          error: null,
        },
      },
    })

    await waitFor(() => {
      expect(screen.getByTestId("onboarding-details-schedule-button")).toBeInTheDocument()
    })
  })

  it("hides Schedule Interview for RBM in the same application state", async () => {
    const appData = createGlobalMockApplication({
      overallStatus: "Shortlisted",
    })
    ;(onboardingService.getApplicationById as jest.Mock).mockResolvedValue(appData)

    renderWithProvidersAndRoute(<ApplicationDetails />, {
      initialEntry: `/applications/${appData.id}`,
      routePath: "/applications/:id",
      preloadedState: {
        auth: {
          user: { id: "rbm-1", name: "RBM", role: "RBM" } as any,
          token: "t",
          isAuthenticated: true,
          loading: false,
          error: null,
        },
      },
    })

    await waitFor(() => {
      expect(screen.queryByTestId("onboarding-details-schedule-button")).not.toBeInTheDocument()
    })
  })

  it("shows progress tab active stage icon for FDD Verification", async () => {
    const ue = userEvent.setup()

    const appData = createGlobalMockApplication({
      overallStatus: SHARED_APP_STATUSES.FDD,
    })
    ;(mockOnboardingService.getApplicationById as jest.Mock).mockResolvedValue(appData)

    renderWithProvidersAndRoute(<ApplicationDetails />, {
      initialEntry: `/applications/${appData.id}`,
      routePath: "/applications/:id",
      preloadedState: {
        auth: {
          user: { id: "admin-1", name: "Admin", role: "DD Admin" } as any,
          token: "t",
          isAuthenticated: true,
          loading: false,
          error: null,
        },
      },
    })

    await ue.click(await screen.findByTestId("onboarding-tab-trigger-progress"))

    const fddIcon = await screen.findByTestId("onboarding-progress-stage-icon-6")
    expect(fddIcon.className).toMatch(/amber-500/)
  })
})
