<?php
// socialmediamanager/app/publisher.php
require_once __DIR__ . "/db.php";
require_once __DIR__ . "/sharepack.php";
function smm_get_tool_row(PDO $pdo, string $toolKey): ?array {
  try {
    $st = $pdo->prepare("SELECT * FROM tools WHERE `key`=? LIMIT 1");
    $st->execute([$toolKey]);
    $t = $st->fetch(PDO::FETCH_ASSOC);
    return $t ?: null;
  } catch (Throwable $e) {
    // If tools table doesn't exist (sqlite build) or schema differs, fail silently.
    return null;
  }
}

function smm_tool_cfg(array $toolRow): array {
  $raw = (string)($toolRow["config_json"] ?? "{}");
  $j = json_decode($raw, true);
  return is_array($j) ? $j : [];
}

/**
 * Send "published" webhook to Zapier.
 * Returns true on success, false otherwise. Never throws.
 */
function smm_zapier_notify_published(PDO $pdo, array $article, array $site, string $url): bool {
  $tool = smm_get_tool_row($pdo, "https://hooks.zapier.com/hooks/catch/26263680/uls9sxe/");
  if (!$tool) return false;

  $cfg = smm_tool_cfg($tool);
  $hook = trim((string)($cfg["url"] ?? ""));
  if ($hook === "") return false;

  // Validate URL early (prevents cURL "bad/illegal format").
  if (!filter_var($hook, FILTER_VALIDATE_URL)) {
    smm_log("Zapier webhook invalid URL: {$hook}", "ERROR");
    return false;
  }

  $timeout = intval($cfg["timeout_sec"] ?? 20);
  if ($timeout <= 0) $timeout = 20;

  // Pull tags + media (best build stores these in tags_json/media_json)
  $tags = [];
  if (!empty($article["tags_json"])) {
    $tmp = json_decode((string)$article["tags_json"], true);
    if (is_array($tmp)) $tags = $tmp;
  }

  $media = [];
  if (!empty($article["media_json"])) {
    $tmp = json_decode((string)$article["media_json"], true);
    if (is_array($tmp)) $media = $tmp;
  }

  $articleId = intval($article["id"] ?? 0);
  $publishedAt = smm_now_iso();

  // Idempotency helpers (Zapier can dedupe if desired)
  $idempotencyKey = sha1($articleId . "|" . $url);
  $requestId = "pub_" . $articleId . "_" . bin2hex(random_bytes(4));

  $payload = [
    "request_id" => $requestId,
    "idempotency_key" => $idempotencyKey,
    "event"      => "published",
    "article_id" => $articleId,
    "title"      => (string)($article["title"] ?? ""),
    "slug"       => (string)($article["slug"] ?? ""),
    "url"        => $url,
    "site_name"  => (string)($site["name"] ?? ""),
    "site_base"  => (string)($site["base_url"] ?? ""),
    "tags"       => $tags,
    "tags_line"  => is_array($tags) ? implode(" ", $tags) : "",
    "image_url"  => (string)($media["image_url"] ?? ""),
    "video_url"  => (string)($media["video_url"] ?? ""),
    "published_at" => $publishedAt,
  ];

  $headers = $cfg["headers"] ?? ["Content-Type"=>"application/json"];
  $hdrs = [];
  if (is_array($headers)) {
    foreach ($headers as $k => $v) {
      $hdrs[] = is_int($k) ? (string)$v : ($k . ": " . $v);
    }
  }
  if (!preg_grep('/content-type/i', $hdrs)) $hdrs[] = "Content-Type: application/json";

  $json = json_encode($payload, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
  if ($json === false) {
    smm_log("Zapier webhook JSON encode failed", "ERROR");
    return false;
  }

  // Some receivers like explicit content-length
  $hdrs[] = "Content-Length: " . strlen($json);

  $ch = curl_init($hook);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
  curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, min(10, $timeout));
  curl_setopt($ch, CURLOPT_HTTPHEADER, $hdrs);
  curl_setopt($ch, CURLOPT_POSTFIELDS, $json);

  $out = curl_exec($ch);
  $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  $err = curl_error($ch);
  curl_close($ch);

  if ($out === false || $code >= 400) {
    smm_log("Zapier webhook failed: HTTP {$code} {$err} " . substr((string)$out, 0, 200), "ERROR");
    return false;
  }

  // Optional success log (keep short)
  smm_log("Zapier webhook sent: HTTP {$code} id={$requestId}", "INFO");
  return true;
}

/**
 * Simple retry wrapper. Never throws, never blocks publish (max ~ (retries*timeout)).
 */
function smm_zapier_notify_published_with_retry(PDO $pdo, array $article, array $site, string $url): void {
  $maxRetries = 2;   // total attempts = 1 + maxRetries
  $retryDelay = 2;   // seconds
  for ($attempt = 0; $attempt <= $maxRetries; $attempt++) {
    $ok = false;
    try {
      $ok = smm_zapier_notify_published($pdo, $article, $site, $url);
    } catch (Throwable $e) {
      // should never throw, but just in case
      smm_log("Zapier notify exception: " . $e->getMessage(), "ERROR");
      $ok = false;
    }
    if ($ok) return;
    if ($attempt < $maxRetries) sleep($retryDelay);
  }
}

function smm_publisher_validate_site(array $site): void {
  $cfg = smm_config();
  $requireAbs = boolval($cfg["publisher_require_absolute_paths"] ?? true);

  foreach (["publish_dir","posts_index_path"] as $k) {
    $v = trim($site[$k] ?? "");
    if ($v === "") throw new Exception("Site missing {$k}");
    if ($requireAbs && !smm_is_absolute_path($v)) throw new Exception("{$k} must be an absolute server path.");
  }
  if (!is_dir($site["publish_dir"])) throw new Exception("publish_dir does not exist: " . $site["publish_dir"]);
  if (!is_writable($site["publish_dir"])) throw new Exception("publish_dir not writable: " . $site["publish_dir"]);

  // posts_index_path directory should be writable or file can be created
  $idxDir = dirname($site["posts_index_path"]);
  if (!is_dir($idxDir)) throw new Exception("posts_index_path directory does not exist: " . $idxDir);
  if (!is_writable($idxDir)) throw new Exception("posts_index_path directory not writable: " . $idxDir);
}



function smm_publish_copy_media_into_post(string $postDir, array $media): array {
  // Copies local media files from /public/media into the post folder so published HTML always renders.
  // Returns updated media array with post-relative URLs (media/<file>).
  $base = realpath(__DIR__ . "/..");
  $publicMedia = ($base ? $base : dirname(__DIR__)) . "/public/media";
  $out = $media;

  $mediaDir = rtrim($postDir, "/") . "/media";
  if (!is_dir($mediaDir)) @mkdir($mediaDir, 0755, true);

  foreach (["image_file"=>"image_url", "video_file"=>"video_url"] as $fileKey => $urlKey) {
    $fn = trim((string)($media[$fileKey] ?? ""));
    if ($fn === "") continue;

    $srcPath = rtrim($publicMedia, "/") . "/" . basename($fn);
    $dstPath = rtrim($mediaDir, "/") . "/" . basename($fn);

    if (is_file($srcPath)) {
      @copy($srcPath, $dstPath);
      $out[$urlKey] = "media/" . basename($fn);
    }
  }
  return $out;
}

function smm_publish_media_html(array $media): string {
  $h = "";
  if (!empty($media["image_url"])) {
    $h .= "<img src=\"" . htmlspecialchars((string)$media["image_url"], ENT_QUOTES) . "\" style=\"max-width:100%;height:auto;border-radius:12px;display:block;margin:14px 0;\" />";
  }
  if (!empty($media["video_url"])) {
    $h .= "<video controls style=\"max-width:100%;border-radius:12px;display:block;margin:14px 0;\"><source src=\"" . htmlspecialchars((string)$media["video_url"], ENT_QUOTES) . "\" type=\"video/mp4\"></video>";
  }
  return $h;
}

function smm_publisher_render_page(array $article, array $site): string {
  // Minimal clean blog wrapper
  $title = htmlspecialchars($article["title"], ENT_QUOTES);
  $body = $article["body_html"];

  return "<!doctype html>
<html lang=\"en\">
<head>
  <meta charset=\"utf-8\">
  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
  <title>{$title}</title>
  <meta name=\"description\" content=\"" . htmlspecialchars(substr(strip_tags($body),0,160), ENT_QUOTES) . "\">
  <style>
    body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial;background:#070b14;color:#e8eefc}
    .wrap{max-width:920px;margin:0 auto;padding:28px 16px}
    a{color:#4f8cff}
    .card{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.08);border-radius:18px;padding:18px}
    h1,h2,h3{line-height:1.15}
    .meta{color:#94a3b8;font-size:.92rem;margin-top:6px}
  </style>
</head>
<body>
  <div class=\"wrap\">
    <div class=\"card\">
      {$body}
      <div class=\"meta\">Published via SocialMediaManager</div>
    </div>
  </div>
</body>
</html>";
}

function smm_publisher_update_posts_index(array $site, array $entry): void {
  $path = $site["posts_index_path"];
  $existing = [];
  if (file_exists($path)) {
    $raw = file_get_contents($path);
    $decoded = json_decode($raw, true);
    if (is_array($decoded)) $existing = $decoded;
  }
  // remove any existing with same slug
  $existing = array_values(array_filter($existing, fn($e)=>($e["slug"] ?? "") !== ($entry["slug"] ?? "")));
  array_unshift($existing, $entry);
  file_put_contents($path, json_encode($existing, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
}

function smm_publish_article(PDO $pdo, int $articleId): array {
  // Load article + site
  $stmt = $pdo->prepare("SELECT * FROM articles WHERE id=:id");
  $stmt->execute([":id"=>$articleId]);
  $a = $stmt->fetch(PDO::FETCH_ASSOC);
  if (!$a) throw new Exception("Article not found: {$articleId}");
  if (!$a["site_profile_id"]) throw new Exception("Article has no site selected.");
  $stmt = $pdo->prepare("SELECT * FROM sites WHERE id=:id");
  $stmt->execute([":id"=>intval($a["site_profile_id"])]);
  $site = $stmt->fetch(PDO::FETCH_ASSOC);
  if (!$site) throw new Exception("Site not found.");

  smm_publisher_validate_site($site);

  $slug = smm_slugify($a["slug"] ?: $a["title"]);
  $dir = rtrim($site["publish_dir"], "/") . "/" . $slug;
  if (!is_dir($dir)) {
    if (!@mkdir($dir, 0755, true)) throw new Exception("Failed to create folder: " . $dir);
  }
  if (!is_writable($dir)) throw new Exception("Folder not writable: " . $dir);

  // Inject media (image/video) if present
$media = [];
if (!empty($a["media_json"])) {
  $tmp = json_decode((string)$a["media_json"], true);
  if (is_array($tmp)) $media = $tmp;
}
$media = smm_publish_copy_media_into_post($dir, $media);
$hero = smm_publish_media_html($media);
$a2 = $a;
if ($hero !== "") $a2["body_html"] = $hero . $a["body_html"];
$html = smm_publisher_render_page($a2, $site);
$outPath = $dir . "/index.html";
  $tags = [];
if (!empty($a["tags_json"])) {
  $tmp = json_decode($a["tags_json"], true);
  if (is_array($tmp)) $tags = $tmp;
}

if ($tags) {
  $tagLine = implode(" ", $tags);
  $html .= "\n<hr>\n<section class=\"tagx-tags\" style=\"margin:18px 0; padding:12px; border-radius:12px; background:rgba(0,0,0,.04);\">\n"
        . "<div style=\"font-weight:700; margin-bottom:6px;\">Tags</div>\n"
        . "<div style=\"line-height:1.6;\">" . htmlspecialchars($tagLine) . "</div>\n"
        . "</section>\n";
}
  file_put_contents($outPath, $html);

  $url = rtrim($site["base_url"], "/") . "/" . $slug . "/";

  // Update posts.json
  $entry = [
    "title" => $a["title"],
    "slug" => $slug,
    "url" => $url,
    "published_at" => smm_now_iso(),
  ];
  smm_publisher_update_posts_index($site, $entry);

  // Mark published
  $now = smm_now_iso();
  $stmt = $pdo->prepare("UPDATE articles SET status='published', slug=:slug, updated_at=:u WHERE id=:id");
  // Auto-create share pack if enabled (kept minimal, does not block publish)
  try {
    $auto = smm_get_setting($pdo, "auto_sharepack_after_publish", "1");
    if (strval($auto) === "1") {
      $platforms = smm_get_setting($pdo, "default_share_platforms", ["facebook","instagram","tiktok","pinterest"]);
      if (!is_array($platforms) || !count($platforms)) $platforms = ["facebook","instagram","tiktok","pinterest"];
      $pack = smm_sharepack_build_from_article($pdo, $a, $site, $platforms);
      smm_sharepack_save($pdo, intval($a["id"]), intval($site["id"]), $pack);
    }
  } catch (Throwable $e) {
    smm_log("SharePack auto-create failed: " . $e->getMessage(), "ERROR");
  }


  $stmt->execute([":slug"=>$slug,":u"=>$now,":id"=>$articleId]);

  // Optional: notify Zapier after publish (never blocks; retries briefly).
  try {
    $a_notify = $a;
    $a_notify["slug"] = $slug;
    smm_zapier_notify_published_with_retry($pdo, $a_notify, $site, $url);
  } catch (Throwable $e) {
    smm_log("Zapier notify error: " . $e->getMessage(), "ERROR");
  }


  return ["ok"=>true,"article_id"=>$articleId,"output_path"=>$outPath,"url"=>$url];
}
