import { resolve } from "path";
import { stringify } from "yaml";
import { CheckOptions, syncFile } from "../utils/common.js";
import { expandFolder } from "../utils/find-area-changed.js";
import {
  PolicyServiceConfig,
  and,
  eventResponderTask,
  hasLabel,
  includesModifiedFiles,
  isAction,
  labelAdded,
  labelRemoved,
  not,
  or,
  payloadType,
} from "./policy.js";
import type { RepoConfig } from "./types.js";

const policyFolder = resolve(process.cwd(), ".github", "policies");

export interface SyncLabelAutomationOptions extends CheckOptions {}

export async function syncLabelAutomation(config: RepoConfig, options: SyncLabelAutomationOptions) {
  await syncPolicyFile(createIssueTriageConfig(config), options);
  await syncPolicyFile(createPrTriageConfig(config), options);
}

async function syncPolicyFile(policy: PolicyServiceConfig, options: CheckOptions) {
  const yaml = stringify(policy);
  const content = `# This file is generated by the sync-labels script DO NOT EDIT manually\n${yaml}`;
  const filename = resolve(policyFolder, `${policy.id}.generated.yml`);
  await syncFile(filename, content, options);
}

function createIssueTriageConfig(config: RepoConfig): PolicyServiceConfig {
  const areaLabels = config.labels.area.labels;
  return {
    id: "issues.triage",
    name: "New Issue Assign labels",
    description: "Assign labels to new issues",
    resource: "repository",
    disabled: false,
    configuration: {
      resourceManagementConfiguration: {
        eventResponderTasks: [
          eventResponderTask({
            description: "Adds `needs-area` label for new unassigned issues",
            if: [
              payloadType("Issues"),
              isAction("Opened"),
              not(and(["isAssignedToSomeone"])),
              not(or(Object.keys(areaLabels).map((area) => hasLabel(area)))),
            ],
            then: [
              {
                addLabel: {
                  label: "needs-area",
                },
              },
            ],
          }),
          eventResponderTask({
            description: "Remove `needs-area` label when an area label is added",
            if: [
              payloadType("Issues"),
              hasLabel("needs-area"),
              "isOpen",
              or(Object.keys(areaLabels).map((area) => labelAdded(area))),
            ],
            then: [
              {
                removeLabel: {
                  label: "needs-area",
                },
              },
            ],
          }),
          eventResponderTask({
            description: "Add `needs-area` back when all area labels are removed",
            if: [
              payloadType("Issues"),
              not(hasLabel("needs-area")),
              "isOpen",
              or(Object.keys(areaLabels).map((area) => labelRemoved(area))),
              not(or(Object.keys(areaLabels).map((area) => hasLabel(area)))),
            ],
            then: [
              {
                addLabel: {
                  label: "needs-area",
                },
              },
            ],
          }),
        ],
      },
    },
  };
}
function createPrTriageConfig(config: RepoConfig): PolicyServiceConfig {
  return {
    id: "prs.triage",
    name: "Assign area labels to PRs",
    description: "Assign area labels to PR depending on path modified.",
    resource: "repository",
    disabled: false,
    configuration: {
      resourceManagementConfiguration: {
        eventResponderTasks: [
          eventResponderTask({
            if: [payloadType("Pull_Request")],
            then: Object.entries(config.areaPaths).map(([label, files]) => {
              const globs = files.map(expandFolder);
              return {
                if: [includesModifiedFiles(globs)],
                then: [
                  {
                    addLabel: {
                      label,
                    },
                  },
                ],
              };
            }),
          }),
        ],
      },
    },
  };
}
