<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
use App\Http\Traits\CommonTrait;
use App\Http\Traits\AgentTrait;
use App\Http\Traits\CryptTrait;
use App\Models\DTO\SearchData;
use App\Notifications\VerifyNotify;

class User extends Authenticatable implements MustVerifyEmail
{
    use HasApiTokens, HasFactory, Notifiable, AgentTrait, CommonTrait, CryptTrait;

    protected $table = 'users';
    protected $primaryKey = 'id';

    const CREATED_AT = 'created_at';
    const UPDATED_AT = 'updated_at';
    const DELETED_AT = 'deleted_at';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'user_group_id',
        'user_grade_id',
        'sid',
        'email',
        'name',
        'nickname',
        'password',
        'today_message',
        'group',
        'grade',
        'phone',
        'birthday',
        'gender',
        'icon',
        'thumb',
        'receive_email',
        'receive_sms',
        'receive_note',
        'register_ip',
        'last_login_at',
        'last_login_ip',
        'is_open_profile',
        'is_email_cert',
        'is_auth_cert',
        'is_adult_cert',
        'is_denied',
        'is_admin',
        'is_withdraw',
        'about_me',
        'email_verified_at'
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'last_login_at' => 'datetime', // 마지막 로그인 일시
        'email_verified_at' => 'datetime', // 이메일 인증일시
        'auth_certified_at' => 'datetime', // 본인 인증일시
        'password_updated_at' => 'datetime' // 비밀번호 변경일시
    ];

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = [
        'last_login_at',
        'created_at',
        'updated_at',
        'deleted_at',
        'email_verified_at',
        'auth_certified_at',
        'password_updated_at'
    ];

    public function userGroup()
    {
        return $this->belongsTo(UserGroup::class)->withDefault();
    }

    public function userGrade()
    {
        return $this->belongsTo(UserGrade::class)->withDefault();
    }

    /**
     * 회원 비밀번호 조회
     */
    public function getAuthPassword(): string
    {
        return $this->password;
    }

    /**
     * 회원 정보 모두 조회
     */
    public function info()
    {
        return Auth::user();
    }

    /**
     * 회원 ID로 조회
     */
    public function findByUserID(string $sid): User
    {
        $query = $this->query();
        $query->where('sid', $sid);

        return $query->firstOrNew();
    }

    /**
     * 회원 이메일로 조회
     */
    public function findByEmail(string $sid): User
    {
        $query = $this->query();
        $query->where('email', $sid);

        return $query->firstOrNew();
    }

    /**
     * 최고 관리자 조회
     */
    public function getMaster(): User
    {
        return $this->where('is_admin', 1)->findOrNew(config('master_key'));
    }

    /**
     * 운영자 조회
     */
    public function getAdmins()
    {
        return $this->where('is_admin', 1)->get();
    }

    /**
     * 회원 ID Unique 생성
     */
    public function getHashKey(): string
    {
        return md5(bin2hex($this->getKey()));
    }

    /**
     * 인증 이메일 반환
     */
    public function getEmailForPasswordReset(): string
    {
        return $this->email;
    }

    /**
     * 자동 로그인 토큰 이름
     */
    public function getRememberTokenName(): string
    {
        return 'remember_token';
    }

    /**
     * 자동 로그인 토큰 조회
     */
    public function getRememberToken(): ?string
    {
        return $this->remember_token;
    }

    /**
     * 관리자 여부 확인
     */
    public function isAdministrator(): bool
    {
        return boolval($this->is_admin);
    }

    /**
     * 인증받은 이메일 조회
     */
    public function getEmailForVerification(): string
    {
        return $this->email;
    }

    /**
     * 이메일 인증을 받았는지 확인
     */
    public function hasVerifiedEmail(): bool
    {
        return (config('use_register_email_auth') ? !is_null($this->email_verified_at) : true);
    }

    /**
     * 이메일 인증 완료 후 값 변경
     */
    public function markEmailAsVerified(): bool
    {
        return $this->forceFill([
            'is_email_cert' => 1,
            'email_verified_at' => $this->freshTimestamp()
        ])->save();
    }

    /**
     * 회원 조회
     */
    public function data(SearchData $params): object
    {
        $query = $this->query();
        $query->select(
            'users.*'
        );

        if ($params->keyword) {
            switch ($params->field) {
                case 'users.id':
                case 'users.sid':
                    $query->where($params->field, '=', $params->keyword);
                    break;
                case 'users.name':
                case 'users.email':
                    $query->where($params->field, 'LIKE', "%{$params->keyword}%");
                    break;
                case 'users.created_at':
                case 'users.last_login_at':
                    $query->whereDate($params->field, $params->keyword);
                    break;
            }
        }

        if($params->receiveEmail) {
            $query->where('users.receive_email', 1);
        }
        if($params->receiveSms) {
            $query->where('users.receive_sms', 1);
        }
        if($params->receiveNote) {
            $query->where('users.receive_note',1);
        }
        if ($params->isAdmin) {
            $query->where('users.is_admin', '=', $params->isAdmin);
        }
        if ($params->isDenied) {
            $query->where('users.is_denied', '=', $params->isDenied);
        }
        if ($params->isWithdraw) {
            $query->where('users.is_withdraw', '=', $params->isWithdraw);
        }

        $query->orderByDesc('users.id');

        $list = $query->paginate($params->perPage, ['*'], 'page', $params->page);
        $total = $this->count();
        $rows = $list->count();

        return (object)[
            'total' => $total,
            'rows' => $rows,
            'list' => $list
        ];
    }

    /**
     * 휴면회원 조회
     */
    public function dormantUsers(SearchData $params): object
    {
        $query = $this->query();
        $query->select(
            'users.*'
        );

        if ($params->keyword) {
            switch ($params->field) {
                case 'users.id':
                case 'users.sid':
                    $query->where($params->field, '=', $params->keyword);
                    break;
                case 'users.name':
                case 'users.email':
                    $query->where($params->field, 'LIKE', "%{$params->keyword}%");
                    break;
                case 'users.created_at':
                case 'users.last_login_at':
                    $query->whereDate($params->field, $params->keyword);
                    break;
            }
        }

        $query->whereNotBetween('last_login_at', [
            now()->subYears(1)->format('Y-m-d 00:00:00'),
            now()->format('Y-m-d 23:59:59')
        ]);

        $query->orderByDesc('users.created_at');

        $list = $query->paginate($params->perPage, ['*'], 'page', $params->page);
        $total = $this->count();
        $rows = $list->count();

        return (object)[
            'total' => $total,
            'rows' => $rows,
            'list' => $list
        ];
    }

    /**
     * 회원가입
     */
    public function register(array $data): mixed
    {
        return DB::transaction(function () use ($data)
        {
            $sid = current(explode('@', $data['email']));
            $ipAddress = request()->getClientIp();
            $userAgent = request()->headers->get('user-agent');
            $referer = request()->headers->get('referer');
            $device = $this->device($userAgent);
            $browser = $this->browser($userAgent);
            $platform = $this->platform($userAgent);
            $robot = $this->robot($userAgent);
            $language = $this->languages();

            $user = User::create([
                'sid' => $sid,
                'email' => $data['email'],
                'name' => null,
                'nickname' => $data['nickname'],
                'phone' => null,
                'birthday' => null,
                'gender' => null,
                'nationality' => null,
                'password' => Hash::make($data['password']),
                'register_ip' => $ipAddress,
                'ci' => null,
                'updated_at' => null,
                'is_auth_cert' => 1,
                'is_adult_cert' => 0,
                'auth_certified_at' => null
            ]);

            UserRegister::create([
                'user_id' => $user->id,
                'device' => $device,
                'language' => $language,
                'browser' => $browser,
                'platform' => $platform,
                'robot' => $robot,
                'ip_address' => $ipAddress,
                'user_agent' => $userAgent,
                'referer' => $referer
            ]);

            return $user;
        });
    }

    /**
     * 회원 정보 수정
     */
    public function updater(int $uid, array $data): bool
    {
        return $this->find($uid)->update($data);
    }

    /**
     * 중복 이메일 확인
     */
    public function sendVerifyCodeToMail(): void
    {
        $this->notify(new VerifyNotify);
    }

    /**
     * 이메일 인증 정보 변경
     */
    public function generateEmailVerified(string $email): void
    {
        [$sid] = explode('@', $email);

        $this->sid = $sid;
        $this->email = $email;
        $this->email_verified_at = now();
        $this->is_email_cert = 1;
        $this->save();

        $this->sendVerifyCodeToMail();
    }

    /**
     * 회원 포인트(P)를 최신 상태로 변경
     */
    public function updateTotalPoint(int $userID): bool
    {
        $sql = "
            UPDATE users USR
            SET USR.point = (SELECT COALESCE(SUM(P.point), 0) FROM tb_point P WHERE P.user_id = USR.id)
            WHERE USR.id = ?;
        ";

        return DB::statement($sql, [$userID]);
    }

    /**
     * 토스 인증 CI로 회원 정보를 조회
     */
    public function findUserByTossCI(string $ci): User
    {
        return $this->where('ci', $ci)->firstOrNew();
    }

    /**
     * 성인여부 확인 (생년월일로 확인)
     */
    public function isAdult(): bool
    {
        return boolval(now()->diffInYears($this->birthday) > 19);
    }

    /**
     * 회원가입 일별통계
     */
    public function dailyStats(): array
    {
        $sql = '
            SELECT DATE(`created_at`) AS `date`, COUNT(*) AS `value` FROM users GROUP BY `created_at`;
        ';

        return DB::select($sql);
    }

    /**
     * 주간 회원가입자 수
     */
    public function totalWeeklyCount(): int
    {
        $sql = '
            SELECT COUNT(*) AS `count` FROM users WHERE WEEK(`created_at`) = WEEK(CURRENT_TIMESTAMP);
        ';

        return DB::selectOne($sql)->count;
    }

    /**
     * 월별 회원가입자 수
     */
    public function totalMonthlyCount(): int
    {
        $sql = '
            SELECT COUNT(*) AS `count` FROM users WHERE MONTH(`created_at`) = MONTH(CURRENT_TIMESTAMP);
        ';

        return DB::selectOne($sql)->count;
    }

    /**
     * 전체 회원가입자 수
     */
    public function totalUserCount(): int
    {
        return $this->where('is_withdraw', 0)->count();
    }
}
