/**
 * Stage 1: Deterministic goal parser for DroidClaw pipeline.
 *
 * Converts natural-language goals into Android intents or app launches
 * using regex patterns. Zero LLM calls.
 *
 * Patterns are DATA-DRIVEN: only enabled when the device reports that
 * an app supports the corresponding intent scheme (via installed apps
 * with intent capabilities).
 */

import type {
  InstalledApp,
  PipelineResult,
  DeviceCapabilities,
} from "@droidclaw/shared";

// ─── Capability Extraction ───────────────────────────────────

/**
 * Build a DeviceCapabilities object from the device's installed apps.
 * Pre-indexes scheme handlers and supported actions for fast pattern matching.
 */
export function buildCapabilities(apps: InstalledApp[]): DeviceCapabilities {
  const schemeHandlers: Record<string, string[]> = {};
  const supportedActions = new Set<string>();

  for (const app of apps) {
    if (!app.intents) continue;
    for (const intent of app.intents) {
      if (intent.startsWith("VIEW:")) {
        const scheme = intent.slice(5);
        if (!schemeHandlers[scheme]) schemeHandlers[scheme] = [];
        schemeHandlers[scheme].push(app.packageName);
      } else if (intent.startsWith("SENDTO:")) {
        const scheme = intent.slice(7);
        if (!schemeHandlers[scheme]) schemeHandlers[scheme] = [];
        schemeHandlers[scheme].push(app.packageName);
      } else if (intent.startsWith("SEND:")) {
        supportedActions.add(intent);
      } else {
        supportedActions.add(intent);
      }
    }
  }

  return { apps, schemeHandlers, supportedActions };
}

// ─── App Name Resolution ─────────────────────────────────────

function resolveApp(
  name: string,
  apps: InstalledApp[]
): InstalledApp | undefined {
  const lower = name.toLowerCase();
  const exact = apps.find((a) => a.label.toLowerCase() === lower);
  if (exact) return exact;
  return apps.find((a) => a.label.toLowerCase().includes(lower));
}

// ─── Regex Patterns ──────────────────────────────────────────

type PatternMatcher = (
  goal: string,
  caps: DeviceCapabilities
) => PipelineResult | null;

const matchCall: PatternMatcher = (goal, caps) => {
  if (!caps.schemeHandlers["tel"]) return null;
  const m = goal.match(/^(?:call|phone|dial)\s+(.+)$/i);
  if (!m) return null;
  const target = m[1].trim();
  const isNumber = /^[\d\s\+\-\(\)]+$/.test(target);
  if (isNumber) {
    const cleaned = target.replace(/[\s\-\(\)]/g, "");
    return {
      stage: "parser",
      type: "intent",
      intent: {
        intentAction: "android.intent.action.DIAL",
        uri: `tel:${cleaned}`,
      },
    };
  }
  return {
    stage: "parser",
    type: "intent",
    intent: {
      intentAction: "android.intent.action.DIAL",
      uri: `tel:${encodeURIComponent(target)}`,
    },
  };
};

const matchSms: PatternMatcher = (goal, caps) => {
  if (!caps.schemeHandlers["sms"] && !caps.schemeHandlers["smsto"])
    return null;
  const m = goal.match(
    /^(?:text|sms|send\s+(?:a\s+)?(?:text|sms|message)\s+to)\s+([\d\+\-\s\(\)]+?)(?:\s+(?:saying|with|message|that says)\s+(.+))?$/i
  );
  if (!m) return null;
  const number = m[1].replace(/[\s\-\(\)]/g, "");
  const body = m[2]?.trim();
  return {
    stage: "parser",
    type: "intent",
    intent: {
      intentAction: "android.intent.action.SENDTO",
      uri: `smsto:${number}`,
      extras: body ? { sms_body: body } : undefined,
    },
  };
};

const matchWhatsApp: PatternMatcher = (goal, caps) => {
  const hasWa =
    caps.schemeHandlers["whatsapp"] ||
    caps.apps.some((a) => a.packageName === "com.whatsapp");
  if (!hasWa) return null;
  const m = goal.match(
    /^(?:whatsapp|send\s+(?:a\s+)?whatsapp\s+(?:message\s+)?to)\s+([\d\+\-\s\(\)]+?)(?:\s+(?:saying|with|message|that says)\s+(.+))?$/i
  );
  if (!m) return null;
  const number = m[1].replace(/[\s\-\(\)]/g, "");
  const text = m[2]?.trim() ?? "";
  const encoded = text ? `?text=${encodeURIComponent(text)}` : "";
  return {
    stage: "parser",
    type: "intent",
    intent: {
      intentAction: "android.intent.action.VIEW",
      uri: `https://wa.me/${number}${encoded}`,
    },
  };
};

const matchNavigation: PatternMatcher = (goal, caps) => {
  if (!caps.schemeHandlers["google.navigation"]) return null;
  const m = goal.match(
    /^(?:navigate|drive|directions?|take me)\s+to\s+(.+)$/i
  );
  if (!m) return null;
  const place = m[1].trim();
  return {
    stage: "parser",
    type: "intent",
    intent: {
      intentAction: "android.intent.action.VIEW",
      uri: `google.navigation:q=${encodeURIComponent(place)}&mode=d`,
    },
  };
};

const matchMapSearch: PatternMatcher = (goal, caps) => {
  if (!caps.schemeHandlers["geo"]) return null;
  const m = goal.match(
    /^(?:find|search|look for|show me)\s+(.+?)(?:\s+(?:nearby|near me|on maps|around here))$/i
  );
  if (!m) return null;
  const query = m[1].trim();
  if (query.length > 50) return null;
  return {
    stage: "parser",
    type: "intent",
    intent: {
      intentAction: "android.intent.action.VIEW",
      uri: `geo:0,0?q=${encodeURIComponent(query)}`,
    },
  };
};

const matchAlarm: PatternMatcher = (goal, caps) => {
  if (!caps.supportedActions.has("android.intent.action.SET_ALARM"))
    return null;
  const m = goal.match(
    /^set\s+(?:an?\s+)?alarm\s+(?:for|at)\s+(\d{1,2})(?::(\d{2}))?\s*(am|pm)?(?:\s+(?:called|named|labeled|saying)\s+(.+))?$/i
  );
  if (!m) return null;
  let hour = parseInt(m[1], 10);
  const minutes = m[2] ? parseInt(m[2], 10) : 0;
  const ampm = m[3]?.toLowerCase();
  const label = m[4]?.trim();

  if (ampm === "pm" && hour < 12) hour += 12;
  if (ampm === "am" && hour === 12) hour = 0;

  const extras: Record<string, string> = {
    "android.intent.extra.alarm.HOUR": String(hour),
    "android.intent.extra.alarm.MINUTES": String(minutes),
  };
  if (label) extras["android.intent.extra.alarm.MESSAGE"] = label;

  return {
    stage: "parser",
    type: "intent",
    intent: {
      intentAction: "android.intent.action.SET_ALARM",
      extras,
    },
  };
};

const matchTimer: PatternMatcher = (goal, caps) => {
  if (!caps.supportedActions.has("android.intent.action.SET_TIMER"))
    return null;
  const m = goal.match(
    /^set\s+(?:a\s+)?timer\s+(?:for\s+)?(\d+)\s*(seconds?|minutes?|mins?|hours?|hrs?)(?:\s+(?:called|named|labeled|saying)\s+(.+))?$/i
  );
  if (!m) return null;
  let seconds = parseInt(m[1], 10);
  const unit = m[2].toLowerCase();
  if (unit.startsWith("min")) seconds *= 60;
  if (unit.startsWith("hour") || unit.startsWith("hr")) seconds *= 3600;
  const label = m[3]?.trim();

  const extras: Record<string, string> = {
    "android.intent.extra.alarm.LENGTH": String(seconds),
  };
  if (label) extras["android.intent.extra.alarm.MESSAGE"] = label;

  return {
    stage: "parser",
    type: "intent",
    intent: {
      intentAction: "android.intent.action.SET_TIMER",
      extras,
    },
  };
};

const matchEmail: PatternMatcher = (goal, caps) => {
  if (!caps.schemeHandlers["mailto"]) return null;
  const m = goal.match(
    /^(?:email|mail|send\s+(?:an?\s+)?email\s+to)\s+([\w.\-+]+@[\w.\-]+)(?:\s+(?:about|subject|with subject|saying)\s+(.+))?$/i
  );
  if (!m) return null;
  const email = m[1];
  const subject = m[2]?.trim();
  return {
    stage: "parser",
    type: "intent",
    intent: {
      intentAction: "android.intent.action.SENDTO",
      uri: `mailto:${email}`,
      extras: subject
        ? { "android.intent.extra.SUBJECT": subject }
        : undefined,
    },
  };
};

const matchOpenApp: PatternMatcher = (goal, caps) => {
  const m = goal.match(
    /^(?:open|launch|start|go to)\s+(?:the\s+)?(.+?)(?:\s+app)?$/i
  );
  if (!m) return null;
  const name = m[1].trim();

  if (/^https?:\/\//i.test(name)) {
    return { stage: "parser", type: "open_url", url: name };
  }

  const app = resolveApp(name, caps.apps);
  if (app) {
    return { stage: "parser", type: "launch", packageName: app.packageName };
  }
  return null;
};

const matchSettings: PatternMatcher = (goal, _caps) => {
  const settingKeywords: Record<string, string> = {
    wifi: "wifi",
    "wi-fi": "wifi",
    bluetooth: "bluetooth",
    display: "display",
    brightness: "display",
    sound: "sound",
    volume: "sound",
    battery: "battery",
    location: "location",
    gps: "location",
    apps: "apps",
    applications: "apps",
    date: "date",
    time: "date",
    accessibility: "accessibility",
    developer: "developer",
    "do not disturb": "dnd",
    dnd: "dnd",
    network: "network",
    storage: "storage",
    security: "security",
  };

  const m = goal.match(/^(?:open\s+)?(.+?)\s+settings$/i);
  if (!m) return null;
  const key = m[1].trim().toLowerCase();
  const setting = settingKeywords[key];
  if (setting) {
    return { stage: "parser", type: "open_settings", setting };
  }
  return null;
};

const matchOpenUrl: PatternMatcher = (goal, _caps) => {
  const m = goal.match(
    /^(?:open|go to|visit|navigate to)\s+(https?:\/\/\S+)$/i
  );
  if (!m) return null;
  return { stage: "parser", type: "open_url", url: m[1] };
};

// ─── Pattern Registry ────────────────────────────────────────

const PATTERNS: PatternMatcher[] = [
  matchOpenUrl,
  matchCall,
  matchSms,
  matchWhatsApp,
  matchEmail,
  matchNavigation,
  matchMapSearch,
  matchAlarm,
  matchTimer,
  matchSettings,
  matchOpenApp,
];

// ─── Public API ──────────────────────────────────────────────

/**
 * Stage 1: Parse a goal deterministically.
 * Returns a PipelineResult if a pattern matches, or { type: "passthrough" }
 * if no pattern matches and the goal should proceed to Stage 2.
 */
export function parseGoal(
  goal: string,
  caps: DeviceCapabilities
): PipelineResult {
  const trimmed = goal.trim();
  if (!trimmed) return { stage: "parser", type: "done", reason: "Empty goal" };
  for (const matcher of PATTERNS) {
    const result = matcher(trimmed, caps);
    if (result) return result;
  }
  return { stage: "parser", type: "passthrough" };
}
