<?php

namespace App\Http\Controllers\Web\Cs;

use App\Http\Controllers\Controller;
use App\Rules\RecaptchaV3Rule;
use App\Services\QnaService;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;

final class CsQnaController extends Controller
{
    public function __construct(
        private readonly QnaService $qnaService
    ) {}

    // 글쓰기 폼
    public function create(Request $request)
    {
        $codes = $this->qnaService->getEnquiryCodes();

        return view('web.cs.qna.index', [
            'enquiryCodes' => $codes,   // select 옵션
        ]);
    }

    // 등록
    public function store(Request $request)
    {
        // legacy.auth가 세션에 _sess 넣어준다 했으니 그대로 사용
        $sess = (array) $request->session()->get('_sess', []);
        $memNo = (int)($sess['_mno'] ?? 0);

        if ($memNo <= 0) {
            abort(403);
        }

        /**
         * 0) 레이트리밋(봇/대량 등록 방어)
         * - 회원번호 + IP 기준으로 1분당 5회, 10분당 20회 같은 식으로 강하게 제한
         */
        $ip = (string) $request->ip();
        $key = 'qna:create:' . $memNo . ':' . $ip;

        if (RateLimiter::tooManyAttempts($key, 5)) {
            abort(429, '요청이 너무 많습니다. 잠시 후 다시 시도해 주세요.');
        }
        RateLimiter::hit($key, 60); // 60초


        $rules = [
            'enquiry_code'    => ['required', 'string', 'max:20'],
            'enquiry_title'   => ['required', 'string', 'max:60'],
            'enquiry_content' => ['required', 'string', 'max:5000'],
            'return_type'     => ['nullable', Rule::in(['sms', 'email'])],
            'hp'              => ['nullable', 'size:0'],    //봇공격 대비
        ];

        if (app()->environment('production')) {
            $rules['g-recaptcha-response'] = ['required', new RecaptchaV3Rule('cs_qna_create')];
        }

        $validated = $request->validate($rules);

        /**
         * 2) 정규화/무해화 (XSS/스크립트/이상 문자 방어)
         * - strip_tags는 기본, 추가로 제어문자 제거 + 공백정리
         * - HTML은 아예 저장하지 않는 정책이면 가장 안전
         */
        $validated['enquiry_title'] = $this->sanitizePlainText(
            (string) $validated['enquiry_title'],
            60
        );

        $validated['enquiry_content'] = $this->sanitizePlainText(
            (string) $validated['enquiry_content'],
            5000
        );

        /**
         * 3) “의심 입력” 즉시 차단(선택)
         * - 운영 경험상 스크립트가 박힌 적이 있다 했으니,
         *   script 태그/iframe/onerror/javascript: 같은 패턴은 바로 차단하는게 낫다.
         */
        $badField = null;
        if ($this->looksMalicious($validated['enquiry_title']))   $badField = 'enquiry_title';
        if ($this->looksMalicious($validated['enquiry_content'])) $badField = $badField ?: 'enquiry_content';


        if ($badField) {
            return redirect()->route('web.cs.qna.index')
                ->with('ui_dialog', [
                    'type' => 'alert',
                    'title' => '안내',
                    'message' => '특수 스크립트/태그가 포함되어 등록할 수 없습니다. HTML/스크립트 없이 내용만 작성해 주세요.',
                ]);
        }

        $sess = (array) $request->session()->get('_sess', []);

        $validated['_user_email'] = (string)($sess['_mid'] ?? '');
        $validated['_user_name']  = (string)($sess['_mname'] ?? '');
        $validated['_user_cell_enc'] = (string)($sess['_mcell'] ?? '');
        $validated['_ip'] = (string)($sess['_ip'] ?? $request->ip());


        // 등록 (현재 연도 테이블에 저장)
        $this->qnaService->createQna($memNo, $validated);

        // 등록 후 내 문의내역으로 이동
        return redirect()->route('web.mypage.qna.index')
            ->with('ui_dialog', [
                'type' => 'alert',
                'title' => '안내',
                'message' => '문의가 등록되었습니다.',
            ]);
    }

    private function sanitizePlainText(string $s, int $maxLen): string
    {
        // 1) 유니코드 정규화(가능하면) - 없으면 패스
        if (class_exists(\Normalizer::class)) {
            $s = \Normalizer::normalize($s, \Normalizer::FORM_C) ?: $s;
        }

        // 2) 태그 제거(HTML 저장 자체를 금지하는 정책)
        $s = strip_tags($s);

        // 3) 제어문자 제거(줄바꿈/탭은 허용)
        // \p{C}는 Control chars 포함. \r\n\t는 남김.
        $s = preg_replace('/[\p{C}&&[^\r\n\t]]/u', '', $s) ?? $s;

        // 4) 공백 정리(연속 스페이스/줄바꿈 폭발 방지)
        $s = preg_replace("/[ \t]+/u", ' ', $s) ?? $s;
        $s = preg_replace("/\n{4,}/u", "\n\n\n", $s) ?? $s;

        $s = trim($s);

        // 5) 길이 자르기(멀티바이트 안전)
        return Str::limit($s, $maxLen, '');
    }

    private function looksMalicious(string $s): bool
    {
        $lower = Str::lower($s);

        // 너무 공격적으로 잡으면 정상 텍스트도 막을 수 있으니, “확실한 것만”
        $patterns = [
            '<script', '</script', '<iframe', '<object', '<embed',
            'javascript:', 'data:text/html', 'vbscript:',
            'onerror=', 'onload=', 'onclick=', 'onmouseover=',
        ];

        foreach ($patterns as $p) {
            if (str_contains($lower, $p)) return true;
        }

        // 특수한 XSS 벡터 (html entity 우회)도 일부 방어
        if (preg_match('/&#x?0*3c;.*script/i', $s)) return true; // "<script" 엔티티 우회

        return false;
    }
}
