<?php

namespace App\Http\Controllers\Admin\Mail;

use App\Http\Controllers\Controller;
use App\Services\Admin\Mail\AdminMailService;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Validator;

final class AdminMailController extends Controller
{
    public function __construct(
        private readonly AdminMailService $service
    ) {}

    public function create()
    {
        $templates = $this->service->getActiveTemplates(); // service가 repo 호출
        $skins = $this->service->getSkinOptions();

        return view('admin.mail.send', [
            'templates' => $templates,
            'skins' => $skins,
        ]);
    }

    public function store(Request $request)
    {
        // ✅ 템플릿 선택값(옵션): Blade에서 <select name="template_id"> 로 보내면 여기서 받음
        $hasTemplate = $request->filled('template_id');

        $rules = [
            'send_mode'       => ['required','in:one,many,csv,db'],

            // ✅ schedule_type 추가 + 예약이면 scheduled_at 필수
            'schedule_type'   => ['required','in:now,schedule'],
            'scheduled_at'    => ['nullable','date_format:Y-m-d H:i','required_if:schedule_type,schedule'],

            'from_email'      => ['required','email','max:190'],
            'from_name'       => ['nullable','string','max:120'],
            'reply_to'        => ['nullable','email','max:190'],

            // ✅ 템플릿 선택이면(스킨 select disabled 등) skin_key가 누락될 수 있으니 nullable 허용 + 서버에서 기본값 세팅
            'skin_key'        => $hasTemplate
                ? ['nullable','in:hero,newsletter,minimal,clean,dark']
                : ['required','in:hero,newsletter,minimal,clean,dark'],

            // 템플릿 선택용(옵션)
            'template_id'     => ['nullable','integer','min:1'],

            // ✅ subject/body는 무조건 들어가야 함(템플릿이면 적용 버튼으로 채워지게)
            'subject'         => ['required','string','max:190'],
            'body'            => ['required','string','max:20000'],

            'hero_image_url'  => ['nullable','string','max:500'],
            'cta_label'       => ['nullable','string','max:80'],
            'cta_url'         => ['nullable','string','max:500'],

            // one
            'to_email'        => ['nullable','email','max:190'],
            'to_name'         => ['nullable','string','max:120'],

            // many(text)
            'to_emails_text'  => ['nullable','string','max:500000'],

            // csv upload
            'to_emails_csv'   => ['nullable','file','mimes:csv,txt','max:5120'],

            // db mode(최소 v1)
            'db_q'            => ['nullable','string','max:120'],
            'db_limit'        => ['nullable','integer','min:1','max:20000'],
        ];

        $v = Validator::make($request->all(), $rules);

        // ✅ 모드별 추가 검증(필수값 체크)
        $v->after(function ($validator) use ($request) {
            $mode = (string)$request->input('send_mode');

            if ($mode === 'one') {
                $em = trim((string)$request->input('to_email'));
                if ($em === '') {
                    $validator->errors()->add('to_email', '단건 발송은 수신자 이메일이 필요합니다.');
                }
            }

            if ($mode === 'many') {
                $raw = (string)$request->input('to_emails_text');
                if (trim($raw) === '') {
                    $validator->errors()->add('to_emails_text', '여러건 발송은 수신자 목록을 입력하세요.');
                }
            }

            if ($mode === 'csv') {
                if (!$request->file('to_emails_csv')) {
                    $validator->errors()->add('to_emails_csv', 'CSV 발송은 CSV 파일 업로드가 필요합니다.');
                }
            }
        });

        $data = $v->validate();

        // ✅ skin_key가 폼에서 누락되면(템플릿 선택 시 disabled 등) 기본값 채움
        //    (템플릿이면 서비스가 템플릿의 skin_key로 덮어쓰도록 하는 게 정석)
        $data['skin_key'] = (string)($data['skin_key'] ?? '');
        if ($data['skin_key'] === '') {
            $data['skin_key'] = 'clean';
        }

        // ✅ 컨트롤러에서 “정상 규칙”으로 파싱 결과를 만들어 request에 심어둠
        //    → 서비스에서 이 parsed_rows를 우선 사용하면 토큰 밀림(덮어쓰기) 문제를 확실히 잡을 수 있음.
        if ($data['send_mode'] === 'many') {
            $rows = $this->parseManyText((string)($data['to_emails_text'] ?? ''));
            if (count($rows) < 1) {
                return back()->with('toast', [
                    'type'=>'danger','title'=>'실패','message'=>'여러건 수신자 파싱 결과가 없습니다. (이메일 형식/중복/입력 형식 확인)'
                ])->withInput();
            }

            // request에 전달(서비스에서 우선 사용)
            $request->request->set('parsed_rows', $rows);
        }

        if ($data['send_mode'] === 'csv') {
            /** @var UploadedFile|null $file */
            $file = $request->file('to_emails_csv');
            $rows = $file ? $this->parseCsvFile($file) : [];
            if (count($rows) < 1) {
                return back()->with('toast', [
                    'type'=>'danger','title'=>'실패','message'=>'CSV 파싱 결과가 없습니다. (1열 이메일 형식/중복/파일 내용 확인)'
                ])->withInput();
            }

            $request->request->set('parsed_rows', $rows);
        }

        $adminId = (int)auth('admin')->id();
        $ctx = [
            'admin_name' => (string)(auth('admin')->user()->name ?? ''),
            'ip' => (string)$request->ip(),
            'ua' => (string)$request->userAgent(),
        ];

        $res = $this->service->createBatch($adminId, $data, $request, $ctx);

        if (!$res['ok']) {
            return back()->with('toast', [
                'type'=>'danger','title'=>'실패','message'=>$res['message'] ?? '처리에 실패했습니다.'
            ])->withInput();
        }

        return redirect()->route('admin.mail.logs.show', ['batchId'=>$res['batch_id']])
            ->with('toast', [
                'type'=>'success','title'=>'접수 완료',
                'message'=>"총 {$res['total']}건 / 유효 {$res['valid']}건 등록. " . ($res['scheduled'] ? '예약 발송입니다.' : '발송을 시작합니다.')
            ]);
    }

    public function preview(Request $request)
    {
        $data = $request->validate([
            'skin_key' => ['required','string','max:40'],
            'subject'  => ['nullable','string','max:200'],
            'body'     => ['nullable','string','max:20000'],
            'heroUrl'  => ['nullable','string','max:500'],
        ]);

        // ✅ 미리보기 샘플도 실제 규칙과 동일하게
        // {_text_02_}=이름, {_text_03_}=금액, {_text_04_}=상품유형
        $sample = [
            '{_text_02_}' => '홍길동',
            '{_text_03_}' => '10000',
            '{_text_04_}' => '쿠폰',
        ];

        $subject = strtr((string)($data['subject'] ?? ''), $sample);
        $body    = strtr((string)($data['body'] ?? ''), $sample);

        return response()->view('mail.preview', [
            'skin_key'  => $data['skin_key'],
            'brand'     => 'PIN FOR YOU',
            'subtitle'  => '공지/이벤트/안내 메일',
            'logoUrl'   => config('services.brand.logo_url', ''),
            'heroUrl'   => (string)($data['heroUrl'] ?? ''),
            'subject'   => $subject,
            'body_html' => nl2br(e($body)),
            'siteUrl'   => config('app.url'),
            'year'      => date('Y'),
            'sns' => [
                ['label'=>'Instagram','url'=>'https://example.com'],
                ['label'=>'YouTube','url'=>'https://example.com'],
                ['label'=>'Kakao','url'=>'https://example.com'],
            ],
        ]);
    }

    // =========================
    // Token helpers (컨트롤러 공통)
    // =========================
    private function buildVarsFromTokens(array $tokens): array
    {
        $vars = [];
        $tokens = array_values($tokens);

        foreach ($tokens as $i => $v) {
            $key = sprintf('{_text_%02d_}', $i + 2); // ✅ 무조건 02부터
            $vars[$key] = trim((string)$v);
        }

        return $vars;
    }

    private function applyVars(string $tpl, array $vars): string
    {
        return strtr($tpl, $vars);
    }

    private function bodyToHtml(string $text): string
    {
        // ✅ 줄바꿈 유지 + XSS 방지
        return nl2br(e($text));
    }

    // =========================
    // Parsers
    // =========================
    private function parseManyText(string $raw): array
    {
        $lines = preg_split("/\r\n|\r|\n/", $raw) ?: [];
        $rows = [];
        $seen = [];

        foreach ($lines as $idx => $line) {
            $line = trim((string)$line);
            if ($line === '') continue;

            // ✅ 빈열 유지(열 밀림 방지)
            $cols = array_map('trim', explode(',', $line));
            $email = strtolower(trim($cols[0] ?? ''));

            if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) continue;
            if (isset($seen[$email])) continue;
            $seen[$email] = true;

            $tokens = array_slice($cols, 1); // 2열부터
            $vars   = $this->buildVarsFromTokens($tokens);

            $rows[] = [
                'email' => $email,
                'vars'  => $vars,
                'raw'   => $line,
                'line'  => $idx + 1,
            ];
        }

        return $rows;
    }

    private function parseCsvFile(UploadedFile $file): array
    {
        $rows = [];
        $seen = [];

        $handle = fopen($file->getRealPath(), 'rb');
        if (!$handle) return [];

        while (($cols = fgetcsv($handle)) !== false) {
            $cols = array_map(fn($v) => trim((string)$v), $cols);

            $email = strtolower(trim($cols[0] ?? ''));
            if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) continue;
            if (isset($seen[$email])) continue;
            $seen[$email] = true;

            $tokens = array_slice($cols, 1); // 2열부터
            $vars   = $this->buildVarsFromTokens($tokens);

            $rows[] = ['email' => $email, 'vars' => $vars];
        }

        fclose($handle);
        return $rows;
    }
}
