/**
 * PR-37 Delivery Planning — Release Planner (Part G)
 *
 * Maps MVP/V1/V2/Enterprise/Global phases to concrete releases:
 *   - Release 1.0 (MVP)
 *   - Release 1.1 (MVP polish)
 *   - Release 2.0 (V1)
 *   - Enterprise Edition
 *   - Global Edition
 *
 * Each release includes version info, feature counts, validation gates,
 * release goals, dependency checks, and relative timing.
 *
 * @module pr-37-delivery-planning/release-planner
 * @since PR-37
 */

/**
 * Standard validation gates applied to every release.
 *
 * @type {string[]}
 */
const DEFAULT_VALIDATION_GATES = [
  "All ACs pass",
  "Regression test pass",
  "Performance benchmark meet",
  "Security scan pass",
  "UAT sign-off",
];

/**
 * Generate a release plan from SF-02 output.
 *
 * @param {object} opts
 * @param {object} opts.mvpScope — MVP scope from SF-02 (mvp, v1, v2, enterprise, global phases)
 * @param {object[]} opts.priorities — Priority matrix entries (from SF-02)
 * @param {object} opts.dependencies — Dependency graph { nodes, edges }
 * @param {object[]} opts.stories — User stories (from SF-02 userStories)
 * @param {object[]} opts.epics — Epics (from SF-02)
 * @param {object[]} opts.features — Features (from SF-02)
 * @returns {object} Release plan
 *
 * @example
 * ```js
 * const plan = generateReleasePlan({
 *   mvpScope: sf02MvpScope,
 *   priorities: sf02Priorities,
 *   dependencies: sf02Graph,
 *   stories: sf02Stories,
 *   epics: sf02Epics,
 *   features: sf02Features,
 * });
 * ```
 */
export function generateReleasePlan({ mvpScope, priorities, dependencies, stories, epics, features }) {
  // ── Input validation ──
  if (!stories || stories.length === 0) {
    return {
      releases: [],
      warnings: ["No stories provided; no releases generated"],
    };
  }

  const warnings = [];
  const priorityMap = new Map();
  for (const p of (priorities || [])) {
    if (p.itemType === "story") {
      priorityMap.set(p.itemId, p.category);
    }
  }

  // Build lookup maps
  const storyMap = new Map();
  for (const s of (stories || [])) storyMap.set(s.id, s);

  const featureMap = new Map();
  for (const f of (features || [])) featureMap.set(f.id, f);

  const epicMap = new Map();
  for (const e of (epics || [])) epicMap.set(e.id, e);

  const dependencyEdges = (dependencies && dependencies.edges) ? dependencies.edges : [];

  /**
   * Map phase scope from MVP scope → actual story/feature/epic IDs.
   */
  function resolvePhaseStories(phase) {
    if (!phase || !phase.mustHaveStories) return [];
    return phase.mustHaveStories.map(s => s.id || s);
  }

  function resolvePhaseEpics(phase, epics) {
    if (!phase || !phase.epics) return [];
    // If epics is a count, we can't resolve IDs; return empty
    if (typeof phase.epics === "number") return [];
    return [...phase.epics].slice(0);
  }

  function resolvePhaseFeatures(phase, features) {
    if (!phase || !phase.features) return [];
    if (typeof phase.features === "number") return [];
    return [...phase.features].slice(0);
  }

  // ── Helper: filter stories by priority ──
  function filterStoriesByPriority(categories) {
    const ids = [];
    for (const s of (stories || [])) {
      const cat = priorityMap.get(s.id) || s.priority || "Could Have";
      if (categories.includes(cat)) ids.push(s.id);
    }
    return ids;
  }

  // ── Helper: find stories with critical unresolved deps ──
  function findCriticalUnresolved(storyIds, availableIds) {
    const available = new Set(availableIds);
    const issues = [];
    for (const sid of storyIds) {
      const story = storyMap.get(sid);
      if (!story) continue;
      const deps = story.dependencies || [];
      for (const depId of deps) {
        if (!available.has(depId)) {
          const depStory = storyMap.get(depId);
          issues.push({
            storyId: sid,
            storyStatement: story.statement || sid,
            unresolvedDepId: depId,
            unresolvedDepStatement: depStory ? depStory.statement : depId,
          });
        }
      }
    }
    // Also check dependency graph edges
    const edgeIssues = [];
    const storyIdSet = new Set(storyIds);
    for (const edge of dependencyEdges) {
      if (storyIdSet.has(edge.to)) {
        const fromStory = storyMap.get(edge.from);
        const toStory = storyMap.get(edge.to);
        if (!available.has(edge.from) && fromStory && toStory) {
          edgeIssues.push({
            storyId: edge.to,
            storyStatement: toStory.statement || edge.to,
            unresolvedDepId: edge.from,
            unresolvedDepStatement: fromStory.statement || edge.from,
          });
        }
      }
    }
    return [...issues, ...edgeIssues].slice(0, 5); // limit to 5
  }

  // ── Helper: build release object ──
  function buildRelease({ version, name, description, storyIds, releaseNumber }) {
    const uniqueEpicIds = new Set();
    const uniqueFeatureIds = new Set();
    let storyCount = 0;
    let storyPoints = 0;

    for (const sid of storyIds) {
      const s = storyMap.get(sid);
      if (!s) continue;
      storyCount++;
      storyPoints += s.storyPoints || 1;
      if (s.epicId) uniqueEpicIds.add(s.epicId);
      if (s.featureId) uniqueFeatureIds.add(s.featureId);
    }

    // Resolve epic/feature names
    const epicNames = [...uniqueEpicIds].map(eid => {
      const epic = epicMap.get(eid);
      return epic ? epic.title : eid;
    }).filter(Boolean);

    const featureNames = [...uniqueFeatureIds].map(fid => {
      const feat = featureMap.get(fid);
      return feat ? feat.title : fid;
    }).filter(Boolean);

    // Determine all resolved IDs for dependency check
    const resolvedIds = new Set(storyIds);

    const depIssues = findCriticalUnresolved(storyIds, resolvedIds);

    return {
      version,
      name,
      description,
      features: {
        epicCount: uniqueEpicIds.size,
        epicNames,
        featureCount: uniqueFeatureIds.size,
        featureNames: featureNames.slice(0, 10), // top 10
        storyCount,
        storyPoints,
      },
      validationGates: [...DEFAULT_VALIDATION_GATES],
      releaseGoals: deriveReleaseGoals(name, version, storyIds, storyMap),
      dependencyCheck: depIssues,
      releaseDate: `Release ${releaseNumber}`,
    };
  }

  // ── 1. Release 1.0 (MVP) ──
  // Must Have stories from MVP phase
  const mvpPhase = mvpScope && mvpScope.mvp;
  let mvpStoryIds = [];

  if (mvpPhase && mvpPhase.mustHaveStories && Array.isArray(mvpPhase.mustHaveStories)) {
    mvpStoryIds = mvpPhase.mustHaveStories.map(s => s.id || s);
  }

  // Fallback: filter Must Have from all stories
  if (mvpStoryIds.length === 0) {
    mvpStoryIds = filterStoriesByPriority(["Must Have"]);
  }

  // ── 2. Release 1.1 (MVP polish) ──
  // Remaining Must Have + critical Should Have fixes
  const shouldHaveIds = filterStoriesByPriority(["Should Have"]);
  // Take first ~30% of Should Have for critical fixes
  const criticalShouldHave = shouldHaveIds.slice(0, Math.max(1, Math.ceil(shouldHaveIds.length * 0.3)));

  // Remaining Must Have stories not in MVP
  const remainingMustHave = mvpStoryIds.filter(id => !mvpStoryIds.includes(id));

  // ── 3. Release 2.0 (V1) ──
  // All Should Have stories that aren't in 1.1
  const v1StoryIds = shouldHaveIds.filter(id => !criticalShouldHave.includes(id));

  // ── 4. Enterprise Edition ──
  const enterprisePhase = mvpScope && mvpScope.enterprise;
  let enterpriseStoryIds = [];
  if (enterprisePhase && enterprisePhase.stories) {
    enterpriseStoryIds = [...enterprisePhase.stories];
  }

  // ── 5. Global Edition ──
  const globalPhase = mvpScope && mvpScope.global;
  let globalStoryIds = [];
  if (globalPhase && globalPhase.stories) {
    globalStoryIds = [...globalPhase.stories];
  }

  // Remove duplicates: Enterprise/Global should contain stories not in earlier releases
  const v2CouldIds = filterStoriesByPriority(["Could Have"]);
  if (enterpriseStoryIds.length === 0) {
    // Fallback: Could Have stories for Enterprise
    enterpriseStoryIds = v2CouldIds.slice(0, Math.ceil(v2CouldIds.length * 0.6));
  }
  if (globalStoryIds.length === 0) {
    globalStoryIds = v2CouldIds.slice(Math.ceil(v2CouldIds.length * 0.6));
  }

  // ── Assemble releases ──
  const releases = [];

  if (mvpStoryIds.length > 0) {
    releases.push(buildRelease({
      version: "1.0.0",
      name: "MVP",
      description: "Minimum Viable Product — core Must Have functionalities for initial market validation",
      storyIds: mvpStoryIds,
      releaseNumber: 1,
    }));
  }

  if (criticalShouldHave.length > 0 || remainingMustHave.length > 0) {
    const polishIds = [...new Set([...criticalShouldHave, ...remainingMustHave])];
    releases.push(buildRelease({
      version: "1.1.0",
      name: "MVP Polish",
      description: "MVP refinement — remaining Must Have items and critical Should Have fixes",
      storyIds: polishIds,
      releaseNumber: 2,
    }));
  }

  if (v1StoryIds.length > 0) {
    releases.push(buildRelease({
      version: "2.0.0",
      name: "V1",
      description: "First full version — complete Should Have feature set for competitive feature parity",
      storyIds: v1StoryIds,
      releaseNumber: 3,
    }));
  }

  if (enterpriseStoryIds.length > 0) {
    releases.push(buildRelease({
      version: "3.0.0",
      name: "Enterprise Edition",
      description: "Enterprise-grade features — advanced security, multi-tenancy, compliance, and admin capabilities",
      storyIds: enterpriseStoryIds,
      releaseNumber: 4,
    }));
  }

  if (globalStoryIds.length > 0) {
    releases.push(buildRelease({
      version: "4.0.0",
      name: "Global Edition",
      description: "Global-scale features — i18n, multi-region, compliance, and localization",
      storyIds: globalStoryIds,
      releaseNumber: 5,
    }));
  }

  // ── Generate warnings ──
  if (mvpStoryIds.length === 0) {
    warnings.push("No MVP stories found; Release 1.0 will be empty");
  }
  if (releases.length === 0) {
    warnings.push("No releases could be generated from the provided data");
  }
  for (const rel of releases) {
    if (rel.dependencyCheck.length > 0) {
      warnings.push(`${rel.name} (${rel.version}): ${rel.dependencyCheck.length} stories have unresolved critical dependencies`);
    }
  }

  return {
    releases,
    warnings,
  };
}

/**
 * Derive business-oriented release goals from the story set.
 *
 * @param {string} name — Release name
 * @param {string} version — Semver
 * @param {string[]} storyIds — Stories in this release
 * @param {Map<string, object>} storyMap — Story lookup
 * @returns {string[]} Goals
 */
function deriveReleaseGoals(name, version, storyIds, storyMap) {
  const goals = [];

  switch (name) {
    case "MVP":
      goals.push("Deliver core value proposition to early adopters");
      goals.push("Validate product-market fit with real users");
      goals.push("Establish foundational user identity and access system");
      break;
    case "MVP Polish":
      goals.push("Address critical feedback from MVP users");
      goals.push("Stabilize core features for broader adoption");
      goals.push("Improve reliability and performance to production standards");
      break;
    case "V1":
      goals.push("Achieve feature parity with market competitors");
      goals.push("Provide comprehensive user experience for mainstream adoption");
      goals.push("Enable self-service onboarding and administration");
      break;
    case "Enterprise Edition":
      goals.push("Meet enterprise security and compliance requirements");
      goals.push("Support multi-tenant deployment at scale");
      goals.push("Provide advanced audit, governance, and admin tools");
      break;
    case "Global Edition":
      goals.push("Support internationalization and localization");
      goals.push("Meet global data residency and privacy regulations");
      goals.push("Enable multi-region deployment and failover");
      break;
    default:
      goals.push(`Deliver ${name} (${version}) features to users`);
      break;
  }

  // Add story-specific goal from top stories
  const topStories = storyIds.slice(0, 3);
  for (const sid of topStories) {
    const s = storyMap.get(sid);
    if (s && s.value) {
      goals.push(`Enable: ${s.value.length > 60 ? s.value.slice(0, 60) + "…" : s.value}`);
    }
  }

  return goals;
}

export default { generateReleasePlan };
