<script setup lang="ts">
import { ref, onMounted, watch } from "vue";
import { useRouter } from "vue-router";
import {
  getProjectApplicableRules, subscribeProject, unsubscribeProject,
  listRulebooks, getRule, createProjectRule, deleteRule,
  suppressRuleForProject, unsuppressRuleForProject,
  suppressTopicForProject, unsuppressTopicForProject,
} from "@/api/rulebooks";
import type { ApplicableRules, Rulebook } from "@/api/rulebooks";

const props = defineProps<{ projectId: number }>();
const router = useRouter();
const applicable = ref<ApplicableRules | null>(null);
const allRulebooks = ref<Rulebook[]>([]);
const showPicker = ref(false);
const expandedRuleIds = ref<Set<number>>(new Set());

const ruleDetails = ref<Record<number, { why: string; how_to_apply: string }>>({});

const showProjectRuleForm = ref(false);
const newProjectRule = ref({ title: "", statement: "", why: "", how_to_apply: "" });

async function load() {
  applicable.value = await getProjectApplicableRules(props.projectId);
}

async function loadAllRulebooks() {
  allRulebooks.value = await listRulebooks();
}

async function subscribe(rulebookId: number) {
  await subscribeProject(props.projectId, rulebookId);
  showPicker.value = false;
  await load();
}

async function unsubscribe(rulebookId: number) {
  if (!confirm("Unsubscribe from this rulebook for this project?")) return;
  await unsubscribeProject(props.projectId, rulebookId);
  await load();
}

async function toggleRuleExpand(ruleId: number) {
  if (expandedRuleIds.value.has(ruleId)) {
    expandedRuleIds.value.delete(ruleId);
  } else {
    expandedRuleIds.value.add(ruleId);
    if (!ruleDetails.value[ruleId]) {
      const rule = await getRule(ruleId);
      ruleDetails.value[ruleId] = {
        why: rule.why || "",
        how_to_apply: rule.how_to_apply || "",
      };
    }
  }
  // trigger reactivity on Set mutation
  expandedRuleIds.value = new Set(expandedRuleIds.value);
}

function openInRulesView(rulebookId: number, ruleId?: number) {
  const query: Record<string, string> = { rb: String(rulebookId) };
  if (ruleId) query.rule = String(ruleId);
  router.push({ path: "/rules", query });
}

interface TopicGroup {
  topic_id: number;
  topic_title: string;
  rules: ApplicableRules["rules"];
}
interface RulebookGroup {
  rulebook_id: number;
  rulebook_title: string;
  topics: TopicGroup[];
}
function groupByRulebookAndTopic(rules: ApplicableRules["rules"]): RulebookGroup[] {
  const byRulebook = new Map<number, RulebookGroup>();
  for (const r of rules) {
    let rb = byRulebook.get(r.rulebook_id);
    if (!rb) {
      rb = { rulebook_id: r.rulebook_id, rulebook_title: r.rulebook_title, topics: [] };
      byRulebook.set(r.rulebook_id, rb);
    }
    let topic = rb.topics.find((t) => t.topic_id === r.topic_id);
    if (!topic) {
      topic = { topic_id: r.topic_id, topic_title: r.topic_title, rules: [] };
      rb.topics.push(topic);
    }
    topic.rules.push(r);
  }
  return Array.from(byRulebook.values());
}

async function submitProjectRule() {
  const statement = newProjectRule.value.statement.trim();
  if (!statement) return;
  await createProjectRule(props.projectId, {
    statement,
    title: newProjectRule.value.title.trim() || undefined,
    why: newProjectRule.value.why.trim() || undefined,
    how_to_apply: newProjectRule.value.how_to_apply.trim() || undefined,
  });
  newProjectRule.value = { title: "", statement: "", why: "", how_to_apply: "" };
  showProjectRuleForm.value = false;
  await load();
}

async function removeProjectRule(ruleId: number) {
  if (!confirm("Delete this project rule? It will move to the trash.")) return;
  await deleteRule(ruleId);
  await load();
}

const showSuppressed = ref(false);

async function suppressRule(ruleId: number) {
  await suppressRuleForProject(props.projectId, ruleId);
  await load();
}

async function unsuppressRule(ruleId: number) {
  await unsuppressRuleForProject(props.projectId, ruleId);
  await load();
}

async function suppressTopic(topicId: number) {
  await suppressTopicForProject(props.projectId, topicId);
  await load();
}

async function unsuppressTopic(topicId: number) {
  await unsuppressTopicForProject(props.projectId, topicId);
  await load();
}

onMounted(async () => {
  await load();
  await loadAllRulebooks();
});

watch(() => props.projectId, load);
</script>

<template>
  <div class="rules-tab" v-if="applicable">
    <section class="subscribed">
      <h3>Subscribed rulebooks</h3>
      <div class="chips">
        <span
          v-for="rb in applicable.subscribed_rulebooks"
          :key="rb.id"
          class="chip"
        >
          <a @click="openInRulesView(rb.id)">{{ rb.title }}</a>
          <button class="chip-remove" @click="unsubscribe(rb.id)" aria-label="Unsubscribe">×</button>
        </span>
        <button v-if="!showPicker" class="add" @click="showPicker = true">+ Subscribe</button>
        <select
          v-else
          @change="subscribe(Number(($event.target as HTMLSelectElement).value))"
        >
          <option value="">Choose a rulebook…</option>
          <option
            v-for="rb in allRulebooks.filter((rb) => !applicable!.subscribed_rulebooks.some((s) => s.id === rb.id))"
            :key="rb.id"
            :value="rb.id"
          >
            {{ rb.title }}
          </option>
        </select>
      </div>
    </section>

    <section class="project-rules">
      <div class="section-head">
        <h3>Project rules</h3>
        <button
          v-if="!showProjectRuleForm"
          class="add"
          @click="showProjectRuleForm = true"
        >
          + New project rule
        </button>
      </div>
      <form v-if="showProjectRuleForm" class="new-rule-form" @submit.prevent="submitProjectRule">
        <input
          v-model="newProjectRule.title"
          placeholder="Title (optional — derived from statement if blank)"
        />
        <textarea
          v-model="newProjectRule.statement"
          required
          autofocus
          placeholder="Statement (required) — the actionable instruction, 1-2 sentences"
          rows="2"
        ></textarea>
        <textarea
          v-model="newProjectRule.why"
          placeholder="Why (optional) — the rationale"
          rows="2"
        ></textarea>
        <textarea
          v-model="newProjectRule.how_to_apply"
          placeholder="How to apply (optional) — when / where it kicks in"
          rows="2"
        ></textarea>
        <div class="form-buttons">
          <button type="submit">Create</button>
          <button type="button" @click="showProjectRuleForm = false">Cancel</button>
        </div>
      </form>
      <ul v-if="applicable.project_rules && applicable.project_rules.length > 0" class="rule-list">
        <li v-for="r in applicable.project_rules" :key="r.id" class="rule">
          <div class="rule-head" @click="toggleRuleExpand(r.id)">
            <span class="rule-title">{{ r.title }}</span>
            <span class="rule-statement">{{ r.statement }}</span>
          </div>
          <div v-if="expandedRuleIds.has(r.id) && ruleDetails[r.id]" class="rule-detail">
            <div v-if="ruleDetails[r.id].why">
              <strong>Why:</strong> {{ ruleDetails[r.id].why }}
            </div>
            <div v-if="ruleDetails[r.id].how_to_apply">
              <strong>How to apply:</strong> {{ ruleDetails[r.id].how_to_apply }}
            </div>
            <button class="delete-link" @click="removeProjectRule(r.id)">Delete</button>
          </div>
        </li>
      </ul>
      <p
        v-else-if="!showProjectRuleForm"
        class="empty"
      >
        No project-only rules yet.
      </p>
    </section>

    <section class="applicable">
      <h3>Applicable rules</h3>
      <p v-if="applicable.rules.length === 0" class="empty">
        No rules yet — subscribe to a rulebook above, or create one at
        <a @click="router.push('/rules')">Rulebooks</a>.
      </p>
      <div
        v-for="rb in groupByRulebookAndTopic(applicable.rules)"
        :key="rb.rulebook_id"
        class="rb-group"
      >
        <h4>{{ rb.rulebook_title }}</h4>
        <div v-for="topic in rb.topics" :key="topic.topic_id" class="topic-group">
          <h5>
            <span>{{ topic.topic_title }}</span>
            <button
              class="skip-btn"
              :title="`Skip the entire ${topic.topic_title} topic for this project`"
              @click="suppressTopic(topic.topic_id)"
            >× skip topic</button>
          </h5>
          <ul>
            <li v-for="r in topic.rules" :key="r.id" class="rule">
              <div class="rule-head">
                <div class="rule-head-text" @click="toggleRuleExpand(r.id)">
                  <span class="rule-title">{{ r.title }}</span>
                  <span class="rule-statement">{{ r.statement }}</span>
                </div>
                <button
                  class="skip-btn"
                  title="Skip this rule for this project"
                  @click.stop="suppressRule(r.id)"
                >× skip</button>
              </div>
              <div v-if="expandedRuleIds.has(r.id) && ruleDetails[r.id]" class="rule-detail">
                <div v-if="ruleDetails[r.id].why">
                  <strong>Why:</strong> {{ ruleDetails[r.id].why }}
                </div>
                <div v-if="ruleDetails[r.id].how_to_apply">
                  <strong>How to apply:</strong> {{ ruleDetails[r.id].how_to_apply }}
                </div>
                <button
                  class="edit-link"
                  @click="openInRulesView(r.rulebook_id, r.id)"
                >
                  Edit in Rulebook →
                </button>
              </div>
            </li>
          </ul>
        </div>
      </div>
      <p v-if="applicable.truncated" class="truncated">
        Truncated at 50 rules — there are more applicable rules.
      </p>
    </section>

    <section
      v-if="applicable.suppressed_rules.length + applicable.suppressed_topics.length > 0"
      class="suppressed"
    >
      <button class="suppressed-toggle" @click="showSuppressed = !showSuppressed">
        <span>Suppressed ({{ applicable.suppressed_rules.length + applicable.suppressed_topics.length }})</span>
        <span class="caret">{{ showSuppressed ? "▾" : "▸" }}</span>
      </button>
      <div v-if="showSuppressed" class="suppressed-body">
        <ul v-if="applicable.suppressed_topics.length > 0" class="suppressed-list">
          <li v-for="t in applicable.suppressed_topics" :key="`topic-${t.id}`">
            <span class="suppressed-kind">topic</span>
            <span class="suppressed-path">{{ t.rulebook_title }} → {{ t.title }}</span>
            <button class="reenable-btn" @click="unsuppressTopic(t.id)">↻ re-enable</button>
          </li>
        </ul>
        <ul v-if="applicable.suppressed_rules.length > 0" class="suppressed-list">
          <li v-for="r in applicable.suppressed_rules" :key="`rule-${r.id}`">
            <span class="suppressed-kind">rule</span>
            <span class="suppressed-path">{{ r.rulebook_title }} → {{ r.topic_title }} → {{ r.title }}</span>
            <button class="reenable-btn" @click="unsuppressRule(r.id)">↻ re-enable</button>
          </li>
        </ul>
      </div>
    </section>
  </div>
</template>

<style scoped>
.rules-tab { padding: 1rem; }
h3 {
  font-size: 0.9em; opacity: 0.7; text-transform: uppercase; letter-spacing: 0.05em;
  margin-top: 0;
}
.chips { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
.chip {
  display: inline-flex; align-items: center; gap: 0.25rem;
  background: var(--color-primary-bg);
  padding: 0.25rem 0.5rem; border-radius: 999px;
}
.chip a { cursor: pointer; }
.chip-remove { background: none; border: none; cursor: pointer; opacity: 0.5; font-size: 1.1em; }
.chip-remove:hover { opacity: 1; }
.add {
  background: none;
  border: 1px dashed var(--color-border);
  padding: 0.25rem 0.75rem; border-radius: 999px; cursor: pointer;
  color: inherit;
}
select {
  background: var(--color-bg); color: inherit;
  border: 1px solid var(--color-border); border-radius: 6px;
  padding: 0.25rem 0.5rem;
}
.applicable { margin-top: 2rem; }
.rb-group { margin-bottom: 1.5rem; }
.rb-group h4 { font-family: Fraunces, serif; font-style: italic; margin-bottom: 0.5rem; }
/* `.topic-group` is deliberately bare — a namespace for the two h5 rules (this
   one and the flex row further down), with the h5's own margin-top doing the
   separating. Its children assume nothing about it, which is what tells it
   apart from a base rule someone deleted (#2444). */
.topic-group h5 {
  font-size: 0.85em; opacity: 0.7; text-transform: uppercase; letter-spacing: 0.05em;
  margin-top: 0.75rem;
}
ul { list-style: none; padding: 0; margin: 0; }
.rule {
  border-left: 2px solid var(--color-primary);
  padding-left: 0.75rem; margin: 0.5rem 0;
}
.rule-head { cursor: pointer; }
.rule-title { font-weight: 500; }
.rule-statement { display: block; opacity: 0.85; margin-top: 0.25rem; }
.rule-detail {
  margin-top: 0.5rem; padding: 0.5rem;
  background: var(--color-bg); border-radius: 6px;
}
.rule-detail > div { margin-bottom: 0.5rem; }
.edit-link {
  background: none; border: none; cursor: pointer;
  color: var(--color-primary); padding: 0.5rem 0 0 0;
}
.empty, .truncated { opacity: 0.7; font-style: italic; }
.empty a { cursor: pointer; text-decoration: underline; }
.project-rules { margin-top: 1.5rem; }
.section-head { display: flex; justify-content: space-between; align-items: center; }
.new-rule-form {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 0.75rem; margin: 0.5rem 0;
  background: var(--color-bg);
  border: 1px solid var(--color-border); border-radius: 6px;
}
.new-rule-form input, .new-rule-form textarea {
  background: var(--color-surface); color: inherit;
  border: 1px solid var(--color-border); border-radius: 6px;
  padding: 0.5rem; font: inherit; resize: vertical;
}
.rule-list { margin-top: 0.5rem; }
.delete-link {
  background: none; border: none; cursor: pointer;
  color: var(--color-destructive); padding: 0.5rem 0 0 0;
}
/* Per-rule / per-topic suppress affordance — quiet by default, reveal on hover */
.topic-group h5 {
  display: flex; justify-content: space-between; align-items: center; gap: 0.5rem;
}
.rule-head {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 0.5rem;
}
.rule-head-text { flex: 1; cursor: pointer; }
.skip-btn {
  background: none; border: none; cursor: pointer;
  color: var(--color-muted); font-size: 0.75rem;
  padding: 0.1rem 0.4rem; opacity: 0; transition: opacity 0.15s;
  white-space: nowrap;
}
.topic-group h5:hover .skip-btn,
.rule:hover .skip-btn,
.skip-btn:focus { opacity: 1; }
.skip-btn:hover { color: var(--color-destructive); }
/* Suppressed section */
.suppressed { margin-top: 1.5rem; }
.suppressed-toggle {
  display: flex; align-items: center; gap: 0.4rem;
  background: none; border: none; cursor: pointer;
  font-size: 0.85rem; opacity: 0.7; padding: 0.25rem 0; color: inherit;
}
.suppressed-toggle:hover { opacity: 1; }
.suppressed-toggle .caret { font-size: 0.7em; }
.suppressed-body { margin-top: 0.5rem; }
.suppressed-list { padding-left: 0; }
.suppressed-list li {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.25rem 0; opacity: 0.75;
}
.suppressed-kind {
  font-size: 0.7em; text-transform: uppercase; letter-spacing: 0.05em;
  padding: 0.1rem 0.4rem; border-radius: 3px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
}
.suppressed-path { flex: 1; }
.reenable-btn {
  background: none; border: none; cursor: pointer;
  color: var(--color-primary); font-size: 0.85em;
}
.reenable-btn:hover { text-decoration: underline; }
</style>
