<?php
namespace app\mobile\controller;

use think\facade\Db;
use think\exception\HttpResponseException;

class OmsAuth extends OmsBase
{
    public function login()
    {
        return $this->guarded(function () {
            $this->requireMethod('POST');
            $username = $this->requiredText('username', 20);
            $password = $this->requiredText('password', 128, false);
            $code = $this->requiredText('code', 256);
            // 限制键升级：清除旧逻辑累计的系统故障次数，其他缓存不受影响。
            $rateKey = $this->cacheKey('login-failure:v2:' . hash('sha256', $this->request->ip() . ':' . $username));
            if ((int)$this->cache()->get($rateKey, 0) >= 5) $this->fail('尝试次数过多，请 15 分钟后重试', 429);
            // 微信和员工身份均验证后，才允许写入绑定。
            $openid = $this->wechatOpenid($code);
            try {
                $account = $this->lockRegistration(function () use ($username, $password, $openid) {
                    $account = Db::name('auth')->where('username', $username)->find();
                    $admin = Db::name('admin_user')->where('username', $username)->find();
                    $member = Db::name('member_list')->where('eid', $username)->where('status', 1)->find();
                    $employeeOnly = $admin && ((int)$admin['job'] === 79 || (int)$admin['status'] !== 1);
                    $type = $employeeOnly ? 1 : ($account ? (int)$account['user_type'] : ($admin ? ($member ? 2 : 3) : 1));
                    $uid = $employeeOnly ? (int)($member['id'] ?? 0) : ($account ? (int)$account['user_id'] : (int)($member['id'] ?? 0));
                    $adminId = $employeeOnly ? 0 : ($account ? (int)$account['admin_id'] : (int)($admin['id'] ?? 0));
                    if ($account && (int)$account['status'] !== 1) $this->fail('账号已停用或迁移数据需核对，请联系管理员', 403);
                    if ($type === 1) {
                        if (!$member || (int)$member['id'] !== (int)$uid) $this->fail('工号或密码不正确，或账号已停用', 403);
                        $hash = $employeeOnly && $account && (int)$account['user_type'] > 1 ? '' : ($account['password_hash'] ?? '');
                        $identity = trim((string)Db::name('member_interview')->where('m_id', $uid)->value('identity'));
                        $initial = strlen($identity) >= 15 && hash_equals(substr($identity, -6), trim($password));
                        $valid = $hash ? $this->verifyPassword($password, $hash) : $initial;
                    } else {
                        if (!$admin || (int)$admin['status'] !== 1 || (int)$admin['id'] !== $adminId) $this->fail('工号或密码不正确，或账号已停用', 403);
                        $valid = $this->verifyPassword($password, $admin['password']);
                        $initial = false;
                    }
                    if (!$valid) $this->fail('工号或密码不正确', 403);
                    if ($account) $this->assertActive($account);
                    if ($account && !empty($account['openid']) && $account['openid'] !== $openid) $this->fail('该账号已绑定其他微信，请通过“重置微信绑定”解绑后重新认证', 409);
                    $other = Db::name('auth')->where('openid', $openid)->where('username', '<>', $username)->find();
                    if ($other) $this->fail('当前微信已绑定其他员工账号，请先重置原账号的微信绑定', 409);
                    $name = (string)($type === 1 ? $member['ename'] : $admin['truename']);
                    if (!$account) {
                        $id = Db::name('auth')->insertGetId(['username'=>$username, 'name'=>$name, 'user_type'=>$type, 'user_id'=>$uid, 'admin_id'=>$adminId, 'status'=>1, 'openid'=>$openid, 'password_hash'=>$type === 1 ? password_hash($password, PASSWORD_DEFAULT) : null, 'must_change_password'=>0, 'verified_at'=>time(), 'created_at'=>time()]);
                    } else {
                        $id = $account['id'];
                        // 首次认证后升级旧 MD5；以后可直接用微信身份自动登录。
                        $changes = ['openid'=>$openid, 'name'=>$name, 'verified_at'=>time(), 'must_change_password'=>0];
                        if ($employeeOnly) $changes += ['user_type'=>1,'user_id'=>$uid,'admin_id'=>0];
                        if ($type === 1) $changes['password_hash'] = password_hash($password, PASSWORD_DEFAULT);
                        Db::name('auth')->where('id', $id)->update($changes);
                    }
                    return Db::name('auth')->find($id);
                });
            } catch (HttpResponseException $exception) {
                // 只有账号认证失败才累计；微信、证书、数据库异常不计入。
                if ($exception->getResponse()->getCode() === 403) {
                    $this->cache()->set($rateKey, (int)$this->cache()->get($rateKey, 0) + 1, 900);
                }
                throw $exception;
            }
            $this->cache()->delete($rateKey);
            return json(['code' => 0, 'message' => '成功', 'data' => $this->issue($account)]);
        });
    }

    public function resetWechatBinding()
    {
        return $this->guarded(function () {
            $this->requireMethod('POST');
            $username = $this->requiredText('username', 20);
            $identity = $this->requiredText('identity', 32);
            $openid = $this->wechatOpenid($this->requiredText('code', 256));
            $accountKey = $this->cacheKey('reset-wechat:account:' . hash('sha256', $username));
            $ipKey = $this->cacheKey('reset-wechat:ip:' . hash('sha256', $this->request->ip()));
            return $this->lockRegistration(function () use ($username, $identity, $openid, $accountKey, $ipKey) {
                $cache = $this->cache();
                if ((int)$cache->get($accountKey, 0) >= 5 || (int)$cache->get($ipKey, 0) >= 20) $this->fail('尝试次数过多，请 15 分钟后重试', 429);
                $cache->set($accountKey, (int)$cache->get($accountKey, 0) + 1, 900);
                $cache->set($ipKey, (int)$cache->get($ipKey, 0) + 1, 900);
                $account = Db::name('auth')->where('openid', $openid)->find();
                if ($account) $account = $this->normalizeAccount($account);
                $valid = false;
                if ($account && $account['username'] === $username && (int)$account['status'] === 1) {
                    if ((int)$account['user_type'] === 1) {
                        $user = Db::name('member_list')->find($account['user_id']);
                        $stored = (string)Db::name('member_interview')->where('m_id', $account['user_id'])->value('identity');
                        $valid = $user && (int)$user['status'] === 1 && $user['eid'] === $username && strlen($stored) >= 15 && preg_match('/^[0-9]{5}[0-9Xx]$/', $identity) && hash_equals(strtoupper(substr(trim($stored), -6)), strtoupper($identity));
                    } else {
                        $user = Db::name('admin_user')->find($account['admin_id']);
                        $stored = $user ? trim((string)$user['phone']) : '';
                        $valid = $user && (int)$user['status'] === 1 && $user['username'] === $username && preg_match('/^1[0-9]{10}$/', $stored) && hash_equals($stored, $identity);
                    }
                }
                if (!$valid) $this->fail('工号或身份信息不正确，或账号已停用', 403);
                // 用户要求删除整条认证记录；旧凭证因账号不存在而失效。
                Db::name('auth')->where(['id'=>$account['id'], 'openid'=>$openid])->delete();
                $cache->delete($accountKey);
                return json(['code'=>0, 'message'=>'账号已解锁，请重新认证', 'data'=>null]);
            });
        });
    }

    public function wechatLogin()
    {
        return $this->guarded(function () {
            $this->requireMethod('POST');
            $openid = $this->wechatOpenid($this->requiredText('code', 256));
            $account = Db::name('auth')->where('openid', $openid)->find();
            if (!$account) $this->fail('请先使用工号完成首次认证', 401, 1001);
            $this->assertActive($account);
            return json(['code' => 0, 'message' => '成功', 'data' => $this->issue($account)]);
        });
    }
    public function logout()
    {
        return $this->guarded(function () {
            $this->requireMethod('POST');
            $token = $this->bearer();
            $this->cache()->delete($this->cacheKey('session:' . hash('sha256', $token)));
            return json(['code' => 0, 'message' => '成功', 'data' => null]);
        });
    }
    public function changePassword()
    {
        return $this->guarded(function () {
            $this->requireMethod('POST');
            $account = $this->current(true);
            if ((int)$account['user_type'] !== 1) $this->fail('管理账号密码请在 OMS 后台修改', 403);
            $password = $this->requiredText('password', 128, false);
            if (strlen($password) < 8 || !preg_match('/[a-zA-Z]/', $password) || !preg_match('/[0-9]/', $password)) $this->fail('请设置至少 8 位、包含字母和数字的密码');
            $this->lockRegistration(function () use ($account, $password) {
                Db::name('auth')->where('id', $account['id'])->inc('version')->update(['password_hash'=>password_hash($password, PASSWORD_DEFAULT), 'must_change_password'=>0]);
            });
            return json(['code' => 0, 'message' => '成功', 'data' => $this->issue($this->account($account['id']))]);
        });
    }

    public function bindingUrl()
    {
        return $this->guarded(function () {
            $this->requireMethod('POST');
            $account = $this->current(true);
            $base = rtrim((string)env('wxoms.public_base_url', ''), '/');
            if (!preg_match('#^https://www\.cqoms\.com$#', $base) || !env('wxoms.gzh_appid', '') || !env('wxoms.gzh_secret', '')) $this->fail('公众号授权尚未配置，请联系管理员', 503);
            if (strtolower((string)parse_url($this->request->domain(), PHP_URL_HOST)) !== strtolower((string)parse_url($base, PHP_URL_HOST))) $this->fail('公众号授权需要将新版接口部署到已配置的 HTTPS 业务域名，本地预览暂不能完成绑定', 503);
            $state = bin2hex(random_bytes(32));
            $this->cache()->set($this->cacheKey('gzh:' . hash('sha256', $state)), ['id'=>$account['id'], 'version'=>$account['version'], 'session_key'=>$this->cacheKey('session:' . hash('sha256', $this->bearer()))], 600);
            return json(['code' => 0, 'message' => '成功', 'data' => ['url'=>$base . '/mobile/OmsAuth/gzhAuthorize?state=' . $state]]);
        });
    }
    protected function bindingState($state)
    {
        if (!is_string($state) || !preg_match('/^[a-f0-9]{64}$/', $state)) $this->fail('授权链接无效，请返回小程序重试');
        $key = $this->cacheKey('gzh:' . hash('sha256', $state));
        $value = $this->cache()->get($key);
        if (!$value) $this->fail('授权已过期，请返回小程序重试');
        if (empty($value['session_key']) || !$this->cache()->has($value['session_key'])) $this->fail('登录已退出，请重新登录后绑定', 401);
        $account = $this->account($value['id']);
        if ((int)$account['version'] !== (int)$value['version']) $this->fail('授权已失效，请重新发起绑定', 401);
        return $account;
    }
    public function gzhAuthorize()
    {
        return $this->guarded(function () {
            $this->requireMethod('GET');
            $state = $this->request->get('state');
            $this->bindingState($state);
            $callback = rtrim(env('wxoms.public_base_url', ''), '/') . '/mobile/OmsAuth/gzhCallback';
            $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query(['appid'=>env('wxoms.gzh_appid', ''), 'redirect_uri'=>$callback, 'response_type'=>'code', 'scope'=>'snsapi_base', 'state'=>$state]) . '#wechat_redirect';
            return redirect($url)->header(['Cache-Control'=>'no-store', 'Referrer-Policy'=>'no-referrer']);
        });
    }
    public function gzhCallback()
    {
        return $this->guarded(function () {
            $this->requireMethod('GET');
            $state = $this->request->get('state');
            $account = $this->bindingState($state);
            $code = $this->request->get('code');
            if (!is_string($code) || !$code || strlen($code) > 256) $this->fail('授权未完成，请返回小程序重试');
            $data = $this->wechatRequest('https://api.weixin.qq.com/sns/oauth2/access_token', ['appid'=>env('wxoms.gzh_appid', ''), 'secret'=>env('wxoms.gzh_secret', ''), 'code'=>$code, 'grant_type'=>'authorization_code']);
            $this->lockRegistration(function () use ($account, $data, $state) {
                // 在锁内重新检查，避免并发回调重复使用同一个授权 state。
                $this->bindingState($state);
                $other = Db::name('auth')->where('gzhOpenid', $data['openid'])->where('id', '<>', $account['id'])->find();
                if ($other) $this->fail('该公众号身份已绑定其他员工，请联系管理员', 409);
                $existing = Db::name('auth')->where('id', $account['id'])->value('gzhOpenid');
                if ($existing && $existing !== $data['openid']) $this->fail('账号已绑定其他公众号身份，请联系管理员', 409);
                Db::name('auth')->where('id', $account['id'])->update(['gzhOpenid'=>$data['openid']]);
                $this->cache()->delete($this->cacheKey('gzh:' . hash('sha256', $state)));
            });
            $html = '<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>绑定成功</title></head><body style="margin:0;background:#f5f7fa;font-family:sans-serif;color:#263449;text-align:center;padding:72px 24px"><div style="font-size:48px;color:#137e78">✓</div><h1>公众号绑定成功</h1><p>请返回小程序，绑定状态将自动更新。</p><button style="border:0;border-radius:12px;padding:14px 26px;background:#137e78;color:white;font-size:16px" onclick="wx.miniProgram.navigateBack()">返回小程序</button><p style="font-size:13px;color:#8490a2">如无法返回，请使用微信左上角返回按钮。</p><script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script></body></html>';
            return response($html, 200, ['Content-Type'=>'text/html; charset=utf-8', 'Cache-Control'=>'no-store', 'Referrer-Policy'=>'no-referrer']);
        });
    }
}
