const args = Object.fromEntries(
  process.argv.slice(2)
    .map((arg) => arg.replace(/^--/, "").split("="))
    .map(([k, v]) => [k, v ?? "true"])
);

const BASE_URL = args.baseUrl || process.env.BASE_URL || "http://localhost:5000/api";
const PASSWORD = "Admin@123";
const STEP_DELAY_MS = Number(args.delayMs || 500);

const EMAILS = {
  DD_ADMIN: args.ddAdminEmail || "lince@royalenfield.com",
  DEALER: args.dealerEmail || "ramesh_1777038131833@gmail.com",
  ASM: args.asmEmail || "abhishek@royalenfield.com",
  RBM: args.rbmEmail || "manish@royalenfield.com",
  DD_ZM: args.ddZmEmail || "piyush@royalenfield.com",
  ZBH: args.zbhEmail || "manav@royalenfield.com",
  DD_LEAD: args.ddLeadEmail || "jaya@royalenfield.com",
  DD_HEAD: args.ddHeadEmail || "ganesh@royalenfield.com",
  NBH: args.nbhEmail || "yashwin@royalenfield.com",
  LEGAL: args.legalEmail || "legal@royalenfield.com",
};

async function resolveDealerAsmEmail(adminToken, dealerEmail) {
  try {
    const res = await apiRequest("/master/dealer-asm-mappings", "GET", null, adminToken);
    const rows = res?.data?.dealers || [];
    const match = rows.find(
      (d) => String(d?.dealerUser?.email || "").toLowerCase() === String(dealerEmail || "").toLowerCase()
    );
    return match?.assignedAsm?.email || null;
  } catch {
    return null;
  }
}

const ROLE_BY_STAGE = {
  "ASM Review": ["ASM"],
  "RBM Review": ["RBM"],
  "DD ZM Review": ["DD_ZM"],
  "ZBH Review": ["ZBH"],
  "DD Lead Review": ["DD_LEAD"],
  "DD Head Approval": ["DD_HEAD"],
  "NBH Approval": ["NBH"],
  "Legal Clearance": ["LEGAL"],
  "NBH Clearance with EOR": ["NBH"],
};

const delay = (ms = STEP_DELAY_MS) => new Promise((r) => setTimeout(r, ms));

async function apiRequest(endpoint, method = "GET", body = null, token = null) {
  const headers = { "Content-Type": "application/json" };
  if (token) headers.Authorization = `Bearer ${token}`;
  const config = { method, headers };
  if (body) config.body = JSON.stringify(body);

  const response = await fetch(`${BASE_URL}${endpoint}`, config);
  const data = await response.json();
  if (!response.ok) {
    throw new Error(`API Error ${method} ${endpoint}: ${JSON.stringify(data)}`);
  }
  return data;
}

async function login(email, password = PASSWORD) {
  if (!login.cache) login.cache = {};
  if (login.cache[email]) return login.cache[email];
  const data = await apiRequest("/auth/login", "POST", { email, password });
  login.cache[email] = data.token;
  return data.token;
}

async function getRelocationByAnyId(id, token) {
  return apiRequest(`/self-service/relocation/${id}`, "GET", null, token);
}

async function approveCurrentStage(requestId, stageName) {
  const candidateRoles = ROLE_BY_STAGE[stageName] || [];
  if (!candidateRoles.length) {
    throw new Error(`No actor mapping found for stage: ${stageName}`);
  }

  let lastError = null;
  const attempts = [];
  for (const roleKey of candidateRoles) {
    const email = EMAILS[roleKey];
    if (!email) {
      attempts.push(`${roleKey}:missing-email`);
      continue;
    }
    try {
      const token = await login(email);
      const res = await apiRequest(`/self-service/relocation/${requestId}/action`, "POST", {
        action: "APPROVE",
        comments: `${roleKey} approved relocation request.`,
      }, token);
      return { roleKey, email, message: res.message || "Approved" };
    } catch (error) {
      lastError = error;
      attempts.push(`${roleKey}:${error.message}`);
    }
  }
  throw new Error(
    `Approval failed for stage: ${stageName}. Attempts -> ${attempts.join(" | ")}`
    + (lastError ? ` | Last error: ${lastError.message}` : "")
  );
}

async function resolveDealerOutlet(dealerToken) {
  const dashboard = await apiRequest("/dealer/dashboard", "GET", null, dealerToken);
  const outlet = dashboard?.data?.outlets?.[0];
  if (!outlet) throw new Error("No dealer outlet found to create relocation request.");
  return outlet;
}

async function run() {
  try {
    console.log("--- STARTING RELOCATION E2E FLOW ---");
    const adminToken = await login(EMAILS.DD_ADMIN);
    
    // Discover dealer if email is not provided or if the default is stale
    let dealerEmail = EMAILS.DEALER;
    let dealerToken = null;

    try {
      console.log(`Attempting login for dealer: ${dealerEmail}...`);
      dealerToken = await login(dealerEmail, "Dealer@123");
    } catch (e) {
      console.log(`Provided/Default dealer login failed. Searching for an onboarded dealer...`);
      const appsRes = await apiRequest('/onboarding/applications', 'GET', null, adminToken);
      const onboardedApps = appsRes.data.filter(a => (a.overallStatus || a.status || '').toLowerCase() === 'onboarded');
      
      for (const app of onboardedApps) {
        try {
          process.stdout.write(`Testing login for ${app.email}... `);
          const dealerData = await apiRequest('/auth/login', 'POST', { 
            email: app.email, 
            password: 'Dealer@123' 
          });
          dealerToken = dealerData.token;
          dealerEmail = app.email;
          console.log('SUCCESS');
          break;
        } catch (err) {
          console.log('FAILED');
        }
      }
    }

    if (!dealerToken) {
      throw new Error("Could not find or login to any onboarded dealer account.");
    }
    
    EMAILS.DEALER = dealerEmail;
    const asmFromMapping = args.asmEmail || await resolveDealerAsmEmail(adminToken, dealerEmail);
    if (asmFromMapping) {
      EMAILS.ASM = asmFromMapping;
      console.log(`[INFO] Using ASM approver: ${EMAILS.ASM}`);
    } else {
      console.log(`[WARN] Dealer-level ASM not found. Falling back to default ASM email: ${EMAILS.ASM}`);
    }

    let requestId = args.requestId;
    if (!requestId) {
      const outlet = await resolveDealerOutlet(dealerToken);
      console.log(`[STEP 1] Submitting relocation for outlet: ${outlet.name} (${outlet.id})`);
      const createRes = await apiRequest("/self-service/relocation", "POST", {
        outletId: args.outletId || outlet.id,
        relocationType: args.relocationType || "Intercity",
        newAddress: args.newAddress || "Sector 21, New Premises",
        newCity: args.newCity || "Gurugram",
        newState: args.newState || "Haryana",
        reason: args.reason || "Business expansion and better customer access",
        proposedDate: args.proposedDate || new Date().toISOString().split("T")[0],
      }, dealerToken);
      requestId = createRes.requestId;
      console.log(`[STEP 1] Request Created: ${requestId}`);
      await delay();
    } else {
      console.log(`[STEP 1] Resuming request: ${requestId}`);
    }

    let step = 2;
    while (true) {
      const detailsRes = await getRelocationByAnyId(requestId, adminToken);
      const request = detailsRes.request;
      const stage = request.currentStage;
      const status = request.status;

      if (stage === "Completed" || status === "Completed") {
        console.log(`[STEP ${step}] SUCCESS: Relocation request is completed.`);
        break;
      }
      if (stage === "Rejected" || status === "Rejected") {
        throw new Error(`Relocation request is rejected at stage: ${stage}`);
      }

      console.log(`[STEP ${step}] Current Stage: ${stage} | Status: ${status}`);
      const actor = await approveCurrentStage(requestId, stage);
      console.log(`[STEP ${step}] ${actor.roleKey} (${actor.email}) -> ${actor.message}`);
      step++;
      await delay();
    }

    const finalRes = await getRelocationByAnyId(requestId, adminToken);
    console.log("--- VERIFICATION RESULTS ---");
    console.log(`RequestId: ${finalRes.request.requestId || requestId}`);
    console.log(`Final Stage: ${finalRes.request.currentStage}`);
    console.log(`Final Status: ${finalRes.request.status}`);
    console.log("Outcome: RELOCATION FLOW COMPLETED SUCCESSFULLY");
    process.exit(0);
  } catch (error) {
    console.error("Workflow failed:", error.message);
    process.exit(1);
  }
}

run();
