<?php
declare(strict_types=1);

require_once __DIR__ . '/../helpers.php';
require_once __DIR__ . '/../security/_csrf.php';

// POST以外は405
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
    http_response_code(405);
    header('Content-Type: text/plain; charset=UTF-8');
    exit('Method Not Allowed');
}

// CSRF
csrf_check_or_die();

// 入力取得
$hp    = trim((string)($_POST['_hp'] ?? '')); // ハニーポット
$cat   = preg_replace('~[^A-Za-z0-9_\-]+~', '', (string)($_POST['item_category'] ?? ''));
$id    = max(0, (int)($_POST['item_id'] ?? 0));
$title = trim((string)($_POST['item_title'] ?? ''));

$name    = trim((string)($_POST['name'] ?? ''));
$email   = trim((string)($_POST['email'] ?? ''));
$message = trim((string)($_POST['message'] ?? ''));
$extra   = (array)($_POST['extra'] ?? []);

// 戻り先（成功時・ハニーポット時とも）
$backUrl = url_for('/detail', ['category' => $cat, 'id' => $id, 'sent' => 1]) . '#contact-result';

// ハニーポット反応 → 成功扱いで戻す（送信しない）
if ($hp !== '') {
    header('Location: ' . $backUrl, true, 302);
    exit;
}

// バリデーション（最小限）
if ($name === '' || $email === '' || $message === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $_SESSION['tpcms_contact_err'][$cat.'#'.($id>0?(string)$id:'-')] = [
        'code'   => 'invalid',
        'values' => ['name'=>$name, 'email'=>$email, 'message'=>$message, 'extra'=>$extra],
    ];
    header('Location: ' . url_for('/detail', ['category'=>$cat,'id'=>$id,'error'=>'invalid']) . '#contact-result', true, 302);
    exit;
}
$extra = tpcms_normalize_extra($extra);
$__k = $cat.'#'.($id>0?(string)$id:'-');
$shownAt = (int)($_SESSION['tpcms_contact_shown'][$__k] ?? 0);
if ($shownAt === 0 || (time() - $shownAt) < 3) {
    $_SESSION['tpcms_contact_err'][$__k] = ['code'=>'timegate','values'=>['name'=>$name,'email'=>$email,'message'=>$message]];
    header('Location: ' . url_for('/detail', ['category'=>$cat,'id'=>$id,'error'=>'timegate']) . '#contact-result', true, 302);
    exit;
}
if (function_exists('mb_strlen')) {
    if (mb_strlen($name, 'UTF-8') > 200 || mb_strlen($message, 'UTF-8') > 5000) {
        http_response_code(400);
        header('Content-Type: text/plain; charset=UTF-8');
        exit('Bad Request: content too long');
    }
}

// 設定読み込み（settings.json）※無ければ既定
function tpcms_load_settings_for_mail(): array {
    $path = DATA_DIR . '/settings.json';
    $out = [
        'site_name'            => 'tpcms',
        'admin_email'          => '',
        // ★ 管理者宛 件名テンプレート（管理画面から保存）
        'admin_notify_subject' => '',
        'auto_reply'           => false,
        'auto_reply_subject'   => '',
        'auto_reply_body'      => '',
        'mail_signature'       => '',
    ];
    if (is_file($path)) {
        $json = @file_get_contents($path);
        if ($json !== false) {
            $a = json_decode($json, true);
            if (is_array($a)) {
                $out['site_name']            = (string)($a['site_name']            ?? $out['site_name']);
                $out['admin_email']          = (string)($a['admin_email']          ?? $out['admin_email']);
                $out['admin_notify_subject'] = (string)($a['admin_notify_subject'] ?? $out['admin_notify_subject']);
                $out['auto_reply']           = (bool)  ($a['auto_reply']           ?? $out['auto_reply']);
                $out['auto_reply_subject']   = (string)($a['auto_reply_subject']   ?? $out['auto_reply_subject']);
                $out['auto_reply_body']      = (string)($a['auto_reply_body']      ?? $out['auto_reply_body']);
                $out['mail_signature']       = (string)($a['mail_signature']       ?? $out['mail_signature']);
            }
        }
    }
    return $out;
}

// --- 送信制御ユーティリティ ---

/** IPv4は/24、IPv6は/64で集計するための接頭辞文字列を返す */
function tpcms_contact_prefix(string $ip): array {
    // ::ffff:a.b.c.d のようなIPv4マップをIPv4として扱う
    if (str_starts_with($ip, '::ffff:')) {
        $ip = substr($ip, 7);
    }
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
        $p = explode('.', $ip);
        if (count($p) === 4) {
            return ['v4:'.$p[0].'.'.$p[1].'.'.$p[2].'.0/24', 'v4'];
        }
    }
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
        $bin = @inet_pton($ip);
        if ($bin !== false && strlen($bin) === 16) {
            $first64 = substr($bin, 0, 8); // /64 = 8bytes
            $parts = unpack('n4', $first64); // 4×16bit
            $hex = array_map(fn($n)=>sprintf('%x', $n), $parts ?: []);
            return ['v6:'.implode(':', $hex).'/64', 'v6'];
        }
    }
    // 不明はそのまま
    return ['raw:'.$ip, 'raw'];
}

/** 1時間ウィンドウの総量制限：IPv4/24=30回、IPv6/64=60回（保存は data/contact_rate.json） */
function tpcms_rate_check_and_count(string $ip): bool {
    [$prefix, $family] = tpcms_contact_prefix($ip);
    $limit = ($family === 'v4') ? 30 : (($family === 'v6') ? 60 : 30);
    $file  = DATA_DIR . '/contact_rate.json';
    $now   = time();
    $win   = $now - 3600;

    // 読み込み
    $map = [];
    if (is_file($file)) {
        $j = @file_get_contents($file);
        if ($j !== false) {
            $a = json_decode($j, true);
            if (is_array($a)) $map = $a;
        }
    }

    // クリーニング＆判定
    $arr = isset($map[$prefix]) && is_array($map[$prefix]) ? $map[$prefix] : [];
    $arr = array_values(array_filter($arr, fn($t)=> is_int($t) && $t >= $win));
    if (count($arr) >= $limit) {
        // 超過
        return false;
    }
    // 記録して保存
    $arr[] = $now;
    $map[$prefix] = $arr;

    // 保存（排他）
    if (!is_dir(DATA_DIR)) @mkdir(DATA_DIR, 0775, true);
    $tmp = json_encode($map, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
    if ($tmp !== false) {
        @file_put_contents($file, $tmp, LOCK_EX);
        @chmod($file, 0664);
    }
    return true;
}

/** extra[...] を平準化して {key: string} へ（配列は「、」結合・最大1000文字） */
function tpcms_normalize_extra(array $src): array {
    $out = [];
    foreach ($src as $k => $v) {
        // keyは半角英数_-/ のみ
        $key = preg_replace('~[^A-Za-z0-9_\-\/]+~', '', (string)$k);
        if ($key === '') continue;
        if (is_array($v)) {
            $flat = [];
            foreach ($v as $vv) { $flat[] = trim((string)$vv); }
            $val = implode('、', array_filter($flat, fn($s)=> $s !== ''));
        } else {
            $val = trim((string)$v);
        }
        // 長すぎる値は丸める
        if (function_exists('mb_substr')) $val = mb_substr($val, 0, 1000, 'UTF-8');
        else $val = substr($val, 0, 1000);
        if ($val !== '') $out[$key] = $val;
    }
    return $out;
}

$st       = tpcms_load_settings_for_mail();
$siteName = ($st['site_name'] !== '') ? $st['site_name'] : 'tpcms';
$to       = (string)$st['admin_email'];
if ($to === '') {
    $_SESSION['tpcms_contact_err'][$__k] = ['code'=>'mail_config','values'=>['name'=>$name,'email'=>$email,'message'=>$message]];
    header('Location: ' . url_for('/detail', ['category'=>$cat,'id'=>$id,'error'=>'mail_config']) . '#contact-result', true, 302);
    exit;
}
$ip = (string)($_SERVER['REMOTE_ADDR'] ?? '');
if ($ip !== '' && !tpcms_rate_check_and_count($ip)) {
    $_SESSION['tpcms_contact_err'][$__k] = ['code'=>'rate_limit','values'=>['name'=>$name,'email'=>$email,'message'=>$message]];
    header('Location: ' . url_for('/detail', ['category'=>$cat,'id'=>$id,'error'=>'rate_limit']) . '#contact-result', true, 302);
    exit;
}

// メール準備
if (function_exists('mb_language')) mb_language('Japanese');
if (function_exists('mb_internal_encoding')) mb_internal_encoding('UTF-8');

// ヘッダ用サニタイズ（改行除去）
$fromEmail = preg_replace("/[\r\n].*/", '', (string)$st['admin_email']);
$replyTo   = preg_replace("/[\r\n].*/", '', $email);
$fromName  = $siteName;
$encFrom   = function_exists('mb_encode_mimeheader') ? mb_encode_mimeheader($fromName, 'UTF-8') : $fromName;

// 管理者宛（ISO-2022-JP-MS で送る）
// 件名テンプレートを settings.json から取得（未設定時の既定を用意）
$tmpl = trim((string)($st['admin_notify_subject'] ?? ''));
$repl = [
    '{site}'     => $siteName,
    '{title}'    => ($title !== '' ? $title : '詳細ページ'),
    '{id}'       => ($id > 0 ? (string)$id : '-'),
    '{category}' => $cat,
];
$subjectUtf8 = strtr(($tmpl !== '' ? $tmpl : '［{site}］お問い合わせ：{title} (ID:{id})'), $repl);

// 本文をここで組み立て（このブロック内だけで完結させる）
$lines = [];
$lines[] = 'サイト名: ' . $siteName;
$lines[] = '対象: ' . ($title !== '' ? $title . ' (ID:' . ($id > 0 ? (string)$id : '-') . ')' : '(未指定)');
$lines[] = 'カテゴリ: ' . $cat;
$lines[] = '送信者: ' . $name;
$lines[] = 'メール: ' . $email;
$lines[] = 'IP: ' . ((string)($_SERVER['REMOTE_ADDR'] ?? ''));
$lines[] = 'UA: ' . ((string)($_SERVER['HTTP_USER_AGENT'] ?? ''));
$lines[] = '---- メッセージ ----';
$lines[] = $message;

// ---- 管理者宛メール本文（レイアウト変更） ----

// 詳細ページのURL（絶対URLを生成）
$detailPath = url_for('/detail', ['category' => $cat, 'id' => $id]);
$scheme     = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host       = (string)($_SERVER['HTTP_HOST'] ?? '');
$detailAbs  = ($host !== '') ? $scheme . '://' . $host . $detailPath : $detailPath;

// ご指定のフォーマット
$adminLines = [];
$adminLines[] = '■問い合わせ対象';
$adminLines[] = ($title !== '' ? $title : '(未指定)') . ' (ID:' . ($id > 0 ? (string)$id : '-') . ')';
$adminLines[] = $detailAbs;
$adminLines[] = '';
$adminLines[] = '■送信者: ' . $name;
$adminLines[] = '■メール: ' . $email;
$adminLines[] = '■本文：' . $message;
if (!empty($extra)) {
    // 任意のラベル：extra_label[キー] があれば優先
    $labels = (array)($_POST['extra_label'] ?? []);
    foreach ($extra as $ek => $ev) {
        $label = isset($labels[$ek]) ? trim((string)$labels[$ek]) : $ek;
        $adminLines[] = '■' . $label . ': ' . $ev;
    }
}

$bodyUtf8 = implode("\n", $adminLines);

// 本文は CRLF に揃えてから UTF-8 の quoted-printable で送る（JISは使わない）
$bodyUtf8CrLf = str_replace("\n", "\r\n", $bodyUtf8);
$bodyQp = quoted_printable_encode($bodyUtf8CrLf);

// 追加ヘッダ（UTF-8 / quoted-printable）
$headers = [
    'From: ' . $encFrom . ' <' . $fromEmail . '>',
    'Reply-To: ' . $replyTo,
    'MIME-Version: 1.0',
    'Content-Type: text/plain; charset=UTF-8',
    'Content-Transfer-Encoding: quoted-printable',
];
$headersStr = implode("\r\n", $headers);

// 送信（管理者宛）— 件名は UTF-8 のまま
$ok1 = @mb_send_mail($to, $subjectUtf8, $bodyQp, $headersStr);

// 自動返信（settings.jsonに auto_reply がtrue の時のみ）
$ok2 = true;
if (!empty($st['auto_reply'])) {
    // 件名（UTF-8のまま渡す）
    $arSubjectUtf8 = ($st['auto_reply_subject'] !== '')
        ? (string)$st['auto_reply_subject']
        : $siteName . '：お問い合わせを受け付けました';

    // 詳細URL（admin側で計算済みで無ければ再計算）
    if (!isset($detailAbs) || $detailAbs === '') {
        $detailPath = url_for('/detail', ['category' => $cat, 'id' => $id]);
        $scheme     = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
        $host       = (string)($_SERVER['HTTP_HOST'] ?? '');
        $detailAbs  = ($host !== '') ? $scheme . '://' . $host . $detailPath : $detailPath;
    }

    $titleOrDefault = ($title !== '' ? $title : '詳細ページ');
    $idText = ($id > 0 ? (string)$id : '-');

    // 置換マップ（テンプレ用）：{name} / {message} / {site} / {title} / {id} / {category} / {detail_url}
    $repl = [
        '{name}'       => $name,
        '{message}'    => $message,
        '{site}'       => $siteName,
        '{title}'      => $titleOrDefault,
        '{id}'         => $idText,
        '{category}'   => $cat,
        '{detail_url}' => $detailAbs,
        '{extra.*}'  => '', // ダミー（後段の展開で上書き）
    ];

    if (!empty($extra)) {
        foreach ($extra as $ek => $ev) {
            $repl['{extra.'.$ek.'}'] = $ev;
        }
    }

    // 先頭に必ず入れたい「問い合わせ対象」ブロック
    $targetBlock =
        "■問い合わせ対象\r\n" .
        $titleOrDefault . " (ID:" . $idText . ")\r\n" .
        $detailAbs . "\r\n\r\n";

    // 本文テンプレ（未設定なら既定文＋「■お問い合わせ内容」）
    if ((string)$st['auto_reply_body'] !== '') {
        $tpl = (string)$st['auto_reply_body'];
        $arBodyUtf8 = strtr($tpl, $repl);

        // テンプレ内に {extra.*} が一つも無ければ、extra を末尾に自動追記
        if (!empty($extra) && !preg_match('/\{extra\.[^}]+\}/u', $tpl)) {
            $labels = (array)($_POST['extra_label'] ?? []);
            $arBodyUtf8 .= "\r\n";
            foreach ($extra as $ek => $ev) {
                $label = isset($labels[$ek]) ? trim((string)$labels[$ek]) : $ek;
                $arBodyUtf8 .= '■' . $label . ': ' . $ev . "\r\n";
            }
        }

        // テンプレに {detail_url} も「問い合わせ対象」という文言も無ければ、先頭に targetBlock を自動付与
        if (strpos($tpl, '{detail_url}') === false && strpos($tpl, '問い合わせ対象') === false) {
            $arBodyUtf8 = $targetBlock . $arBodyUtf8;
        }

        // {message} が無ければ、末尾に「■お問い合わせ内容」を自動付与
        if (strpos($tpl, '{message}') === false && $message !== '') {
            $arBodyUtf8 .= "\r\n\r\n■お問い合わせ内容\r\n" . $message;
        }
    } else {
        $arBodyUtf8 =
            $name . " 様\r\n\r\n" .
            "お問い合わせありがとうございます。担当者より折り返しご連絡いたします。\r\n\r\n" .
            $targetBlock .
            "■お問い合わせ内容\r\n" .
            $message;
    }

    // 署名は自動返信の末尾に自動付与
    $sig = trim((string)$st['mail_signature']);
    if ($sig !== '') {
        $arBodyUtf8 .= "\r\n\r\n" . $sig;
    }

    // 本文は CRLF に揃えてから UTF-8 の quoted-printable で送る（JISは使わない）
    $arBodyUtf8CrLf = str_replace("\n", "\r\n", $arBodyUtf8);
    $arBodyQp = quoted_printable_encode($arBodyUtf8CrLf);

    $arHeaders = [
        'From: ' . $encFrom . ' <' . $fromEmail . '>',
        'Reply-To: ' . $fromEmail,
        'MIME-Version: 1.0',
        'Content-Type: text/plain; charset=UTF-8',
        'Content-Transfer-Encoding: quoted-printable',
    ];

    // 件名は UTF-8 のまま
    $ok2 = @mb_send_mail($replyTo, $arSubjectUtf8, $arBodyQp, implode("\r\n", $arHeaders));
}

// とりあえず成否にかかわらず元の詳細へ（次ステップでアラート表示検討可）
header('Location: ' . $backUrl, true, 302);
exit;
