<?php
// config/auth.php — 인증·세션·권한 (5단계 역할)
if (session_status() === PHP_SESSION_NONE) session_start();

// 폴더 경로 자동 감지 — 어떤 폴더명이든 자동 적용
if (!defined('BASE_URL')) {
    $scriptPath = str_replace('\\', '/', $_SERVER['SCRIPT_NAME'] ?? '');
    // config/auth.php 기준 → 상위 폴더가 루트
    $configDir = str_replace('\\', '/', __DIR__);
    $docRoot = str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT'] ?? '');
    $rootDir = dirname($configDir); // strategic_ops 폴더 절대경로
    $basePath = str_replace($docRoot, '', $rootDir);
    $basePath = '/' . trim($basePath, '/') . '/';
    define('BASE_URL', $basePath);
}

define('ROLE_LEVEL', [
    'sysadmin'    => 5,
    'ceo'         => 4,
    'manager'     => 3,
    'team_leader' => 2,
    'staff'       => 1,
]);

define('ROLE_LABELS', [
    'sysadmin'    => '시스템관리자',
    'ceo'         => '대표',
    'manager'     => '관리자',
    'team_leader' => '팀장',
    'staff'       => '직원',
]);

/* ── 기본 헬퍼 ── */

function isLoggedIn() {
    return isset($_SESSION['user_id']);
}

function requireLogin() {
    if (!isLoggedIn()) { header('Location: '.BASE_URL.'index.php'); exit; }
    // 임시 비밀번호 → 강제 변경
    if (!empty($_SESSION['must_change_pw']) && basename($_SERVER['PHP_SELF']) !== 'force_change_pw.php') {
        header('Location: '.BASE_URL.'pages/force_change_pw.php');
        exit;
    }
}

function requireRole($roles) {
    requireLogin();
    if (!in_array($_SESSION['user_role'], (array)$roles)) {
        http_response_code(403);
        echo '<!DOCTYPE html><html lang="ko"><head><meta charset="UTF-8">
              <link rel="stylesheet" href=BASE_URL."assets/css/style.css"></head><body>
              <div style="display:flex;justify-content:center;align-items:center;min-height:100vh;">
              <div class="card" style="max-width:420px;text-align:center;padding:40px;">
              <div style="font-size:48px;margin-bottom:16px;">⛔</div>
              <h2 style="color:var(--accent-text);">접근 권한이 없습니다</h2>
              <p style="color:var(--text-secondary);margin:16px 0;">이 페이지에 접근할 수 있는 권한이 부족합니다.</p>
              <a href=BASE_URL."pages/dashboard.php" style="display:inline-block;padding:10px 24px;background:var(--accent);color:#fff;border-radius:4px;text-decoration:none;">대시보드로 돌아가기</a>
              </div></div></body></html>';
        exit;
    }
}

function hasMinRole($minRole) {
    return (ROLE_LEVEL[$_SESSION['user_role']] ?? 0) >= (ROLE_LEVEL[$minRole] ?? 99);
}

function canAssignTeamLeader() {
    return hasMinRole('manager');
}

function currentUser() {
    return [
        'user_id'  => $_SESSION['user_id']  ?? null,
        'staff_id' => $_SESSION['staff_id'] ?? null,
        'name'     => $_SESSION['name']     ?? '',
        'role'     => $_SESSION['user_role'] ?? '',
        'grade'    => $_SESSION['grade']    ?? '',
        'team'     => $_SESSION['team']     ?? '',
    ];
}

function getRoleLabel($role)      { return ROLE_LABELS[$role] ?? $role; }
function getRoleBadgeClass($role) {
    return [
        'sysadmin'    => 'badge-danger',
        'ceo'         => 'badge-warning',
        'manager'     => 'badge-success',
        'team_leader' => 'badge-info',
        'staff'       => 'badge-gray',
    ][$role] ?? 'badge-gray';
}

/* ── 로그인 ── */

function login($db, $login_id, $password) {
    $stmt = $db->prepare("
        SELECT u.user_id, u.staff_id, u.user_role, u.password_hash,
               u.must_change_pw, u.approval_status, u.is_active,
               s.name, s.grade, s.team
        FROM user_account u
        JOIN staff s ON u.staff_id = s.staff_id
        WHERE u.login_id = ?
    ");
    $stmt->execute([$login_id]);
    $u = $stmt->fetch();

    if (!$u) return ['error' => '아이디 또는 비밀번호가 일치하지 않습니다.'];
    if (!password_verify($password, $u['password_hash']))
        return ['error' => '아이디 또는 비밀번호가 일치하지 않습니다.'];
    if ($u['approval_status'] === '대기')
        return ['error' => '가입 승인 대기 중입니다. 시스템 관리자에게 문의하세요.'];
    if ($u['approval_status'] === '반려')
        return ['error' => '가입이 반려되었습니다. 시스템 관리자에게 문의하세요.'];
    if (!$u['is_active'])
        return ['error' => '비활성화된 계정입니다. 시스템 관리자에게 문의하세요.'];

    $_SESSION['user_id']        = $u['user_id'];
    $_SESSION['staff_id']       = $u['staff_id'];
    $_SESSION['name']           = $u['name'];
    $_SESSION['user_role']      = $u['user_role'];
    $_SESSION['grade']          = $u['grade'];
    $_SESSION['team']           = $u['team'];
    $_SESSION['must_change_pw'] = $u['must_change_pw'];

    $db->prepare("UPDATE user_account SET last_login = NOW() WHERE user_id = ?")
       ->execute([$u['user_id']]);

    writeAudit($db, 'user_account', $u['user_id'], 'LOGIN');
    return ['success' => true];
}

function logout() {
    session_destroy();
    header('Location: '.BASE_URL.'index.php');
    exit;
}

/* ── DB에서 역할별 메뉴 가져오기 (menu_permission 테이블) ── */

function getMenusForRole($db, $role, $team = '') {
    $stmt = $db->prepare("
        SELECT menu_key, menu_label, menu_icon, menu_group, menu_url
        FROM menu_permission
        WHERE user_role = ? AND is_visible = 1
        ORDER BY sort_order ASC
    ");
    $stmt->execute([$role]);
    $menus = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // 팀별 메뉴 필터 적용
    if ($team) {
        // 팀 메뉴 분류 필터 (그룹이 특정 팀 전용이면 해당 팀만 표시)
        try {
            $gaStmt = $db->query("SELECT menu_group, GROUP_CONCAT(assigned_team) AS teams FROM team_menu_group_assign GROUP BY menu_group");
            $gaRows = $gaStmt->fetchAll(PDO::FETCH_ASSOC);
            $restrictedGroups = [];
            foreach ($gaRows as $ga) {
                $allowedTeams = explode(',', $ga['teams']);
                if (!in_array($team, $allowedTeams)) {
                    $restrictedGroups[] = $ga['menu_group'];
                }
            }
            if ($restrictedGroups) {
                $menus = array_filter($menus, fn($m) => !in_array($m['menu_group'], $restrictedGroups));
                $menus = array_values($menus);
            }
        } catch(Exception $e) {}

        try {
            // 개별 메뉴 단위 필터
            $tiStmt = $db->prepare("SELECT menu_key FROM team_menu_item_config WHERE team=? AND is_visible=0");
            $tiStmt->execute([$team]);
            $hiddenItems = $tiStmt->fetchAll(PDO::FETCH_COLUMN);
            if ($hiddenItems) {
                $menus = array_filter($menus, fn($m) => !in_array($m['menu_key'], $hiddenItems));
                $menus = array_values($menus);
            }
        } catch(Exception $e) {}

        try {
            // 그룹 단위 필터 (하위호환)
            $tcStmt = $db->prepare("SELECT menu_group FROM team_menu_config WHERE team=? AND is_visible=0");
            $tcStmt->execute([$team]);
            $hiddenGroups = $tcStmt->fetchAll(PDO::FETCH_COLUMN);
            if ($hiddenGroups) {
                $menus = array_filter($menus, fn($m) => !in_array($m['menu_group'], $hiddenGroups));
                $menus = array_values($menus);
            }
        } catch(Exception $e) {}
    }
    return $menus;
}

/* ── 감사 로그 ── */

function writeAudit($db, $table, $recordId, $action, $newValues = null, $oldValues = null) {
    static $columnFixed = false;
    $doInsert = function() use ($db, $table, $recordId, $action, $newValues, $oldValues) {
        $db->prepare("INSERT INTO audit_log (table_name, record_id, action, changed_by, new_values, old_values, ip_address)
            VALUES (?,?,?,?,?,?,?)")
           ->execute([
               $table, $recordId, $action,
               $_SESSION['user_id'] ?? null,
               $newValues ? json_encode($newValues, JSON_UNESCAPED_UNICODE) : null,
               $oldValues ? json_encode($oldValues, JSON_UNESCAPED_UNICODE) : null,
               $_SERVER['REMOTE_ADDR'] ?? ''
           ]);
    };
    try {
        $doInsert();
    } catch (Exception $e) {
        // ENUM 제한으로 실패 시 VARCHAR로 변경 후 재시도
        if (!$columnFixed && (str_contains($e->getMessage(), 'Data truncated') || str_contains($e->getMessage(), '1265') || str_contains($e->getMessage(), 'Incorrect'))) {
            $columnFixed = true;
            try {
                $db->exec("ALTER TABLE audit_log MODIFY COLUMN action VARCHAR(30) NOT NULL");
                $doInsert();
            } catch (Exception $e2) { /* 재시도도 실패 시 무시 */ }
        }
    }
}

/* ── 임시 비밀번호 생성 ── */

function generateTempPassword($length = 10) {
    $chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789!@#';
    $pw = '';
    for ($i = 0; $i < $length; $i++) {
        $pw .= $chars[random_int(0, strlen($chars) - 1)];
    }
    return $pw;
}

/* ── 역할 변경 시 팀장 연동 ── */
function syncTeamLeaderOnRoleChange($db, $staffId, $oldRole, $newRole) {
    if (!$staffId) return;
    // 팀장 → 다른 역할: team_master에서 leader 해제
    if ($oldRole === 'team_leader' && $newRole !== 'team_leader') {
        $db->prepare("UPDATE team_master SET leader_staff_id=NULL WHERE leader_staff_id=?")->execute([$staffId]);
    }
    // 다른 역할 → 팀장: team_master에 leader 설정 (같은 팀 기준)
    if ($oldRole !== 'team_leader' && $newRole === 'team_leader') {
        $team = $db->prepare("SELECT team FROM staff WHERE staff_id=?");
        $team->execute([$staffId]);
        $teamName = $team->fetchColumn();
        if ($teamName) {
            $db->prepare("UPDATE team_master SET leader_staff_id=? WHERE team_name=?")->execute([$staffId, $teamName]);
        }
    }
}

/* ── 메뉴별 권한 조회 ── */
function getMenuPerm($db, $menuKey, $roleKey = null) {
    if (!$roleKey) $roleKey = $_SESSION['user_role'] ?? 'staff';
    try {
        $stmt = $db->prepare("SELECT * FROM menu_role_permission WHERE menu_key=? AND role_key=?");
        $stmt->execute([$menuKey, $roleKey]);
        $p = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($p) return $p;
    } catch(Exception $e) {}
    // 기본값: sysadmin/ceo=전체, manager=팀조회, 나머지=본인
    if (in_array($roleKey, ['sysadmin','ceo'])) return ['view_scope'=>'all','can_create'=>1,'can_edit'=>1,'can_delete'=>1,'need_ceo_approval'=>0,'need_sysadmin_approval'=>0];
    if ($roleKey === 'manager') return ['view_scope'=>'all','can_create'=>0,'can_edit'=>1,'can_delete'=>1,'need_ceo_approval'=>0,'need_sysadmin_approval'=>0];
    return ['view_scope'=>'own','can_create'=>1,'can_edit'=>0,'can_delete'=>0,'need_ceo_approval'=>0,'need_sysadmin_approval'=>1];
}

/* ── 팀장 왕관 이름 표시 헬퍼 ── */
function crownName($name, $role = '') {
    $n = htmlspecialchars($name);
    if (in_array($role, ['team_leader','팀장'])) {
        return '👑 ' . $n;
    }
    return $n;
}

/* ── 인센티브 요율 조회 (grade 기반, 다중 fallback) ── */
function getIncRate($db, $grade) {
    if (!$grade) return null;
    $tries = [
        ["SELECT min_rate,max_rate FROM incentive_rate WHERE grade COLLATE utf8mb4_unicode_ci=? COLLATE utf8mb4_unicode_ci AND is_active=1", [$grade]],
        ["SELECT min_rate,max_rate FROM incentive_rate WHERE grade=? AND is_active=1", [$grade]],
        ["SELECT min_rate,max_rate FROM incentive_rate WHERE grade COLLATE utf8mb4_unicode_ci=? COLLATE utf8mb4_unicode_ci", [$grade]],
        ["SELECT min_rate,max_rate FROM incentive_rate WHERE grade=?", [$grade]],
        ["SELECT min_rate,max_rate FROM incentive_rate WHERE TRIM(grade)=TRIM(?)", [$grade]],
    ];
    foreach ($tries as $t) {
        try { $s=$db->prepare($t[0]); $s->execute($t[1]); $r=$s->fetch(); if($r && $r['min_rate']>0) return $r; } catch(Exception $e) {}
    }
    return null;
}

/* ── 뷰 자동 보장 ── */
function ensureViews($db) {
    static $checked = false;
    if ($checked) return;
    $checked = true;
    // audit_log action 컬럼 항상 VARCHAR 보장
    try { $db->exec("ALTER TABLE audit_log MODIFY COLUMN action VARCHAR(30) NOT NULL"); } catch(Exception $e) {}
    // profit_target_monthly 없으면 자동 추가
    try { $c=$db->query("SELECT COUNT(*) FROM policy_rule WHERE rule_key='profit_target_monthly'")->fetchColumn(); if(!$c) $db->exec("INSERT INTO policy_rule (rule_key,rule_value,description) VALUES('profit_target_monthly','45000000','월간 1인당 이익 목표')"); } catch(Exception $e) {}
    $needRebuild = false;
    try { $db->query("SELECT 1 FROM v_profit_by_person LIMIT 0"); } catch(Exception $e) { $needRebuild = true; }
    if (!$needRebuild) { try { $db->query("SELECT 1 FROM v_incentive_calc LIMIT 0"); $needRebuild = true; } catch(Exception $e) { $needRebuild = true; } }
    if (!$needRebuild) { try { $db->query("SELECT 1 FROM v_pipeline LIMIT 0"); } catch(Exception $e) { $needRebuild = true; } }
    if (!$needRebuild) { try { $db->query("SELECT 1 FROM v_dashboard_monthly LIMIT 0"); } catch(Exception $e) { $needRebuild = true; } }
    if ($needRebuild) {
        try {
            $db->exec("CREATE OR REPLACE VIEW v_profit_by_person AS SELECT s.staff_id,st.name,st.grade,st.team,s.sale_month,COUNT(*) AS deal_count,SUM(s.revenue) AS total_revenue,SUM(s.cost) AS total_cost,SUM(s.net_profit) AS total_net_profit,CASE WHEN COUNT(*)>0 THEN ROUND(SUM(s.revenue)/COUNT(*)) ELSE 0 END AS avg_deal_price FROM sales s JOIN staff st ON s.staff_id=st.staff_id WHERE s.stage IN('계약','촬영','방송완료') AND s.confirm_type='확정' GROUP BY s.staff_id,st.name,st.grade,st.team,s.sale_month");
            $db->exec("CREATE OR REPLACE VIEW v_profit_by_team AS SELECT st.team,s.sale_month,COUNT(*) AS deal_count,SUM(s.revenue) AS total_revenue,SUM(s.cost) AS total_cost,SUM(s.net_profit) AS total_net_profit FROM sales s JOIN staff st ON s.staff_id=st.staff_id WHERE s.stage IN('계약','촬영','방송완료') GROUP BY st.team,s.sale_month");
            $db->exec("CREATE OR REPLACE VIEW v_profit_total AS SELECT s.sale_month,COUNT(*) AS deal_count,SUM(s.revenue) AS total_revenue,SUM(s.cost) AS total_cost,SUM(s.net_profit) AS total_net_profit,CASE WHEN COUNT(*)>0 THEN ROUND(SUM(s.revenue)/COUNT(*)) ELSE 0 END AS avg_deal_price FROM sales s WHERE s.stage IN('계약','촬영','방송완료') GROUP BY s.sale_month");
            $db->exec("CREATE OR REPLACE VIEW v_incentive_calc AS SELECT p.staff_id,p.name,p.grade,p.team,p.sale_month,p.deal_count,p.total_revenue,p.total_net_profit,CASE WHEN p.deal_count>=COALESCE((SELECT CAST(rule_value AS UNSIGNED) FROM policy_rule WHERE rule_key='incentive_min_deals'),3) AND p.total_net_profit>0 AND COALESCE(ir.is_active,1)=1 THEN 'Y' ELSE 'N' END AS eligible,CASE WHEN COALESCE(ir.is_active,1)=1 AND p.total_net_profit>0 THEN ROUND(p.total_net_profit*ir.min_rate) ELSE 0 END AS incentive_min,CASE WHEN COALESCE(ir.is_active,1)=1 AND p.total_net_profit>0 THEN ROUND(p.total_net_profit*ir.max_rate) ELSE 0 END AS incentive_max,ir.min_rate,ir.max_rate,ir.basis AS rate_basis,COALESCE(ir.is_active,1) AS grade_incentive_active,COALESCE(NULLIF(kt.target_deals,0),(SELECT CAST(rule_value AS UNSIGNED) FROM policy_rule WHERE rule_key='deal_target_monthly'),0) AS target_deals,COALESCE(NULLIF(kt.target_amount,0),(SELECT CAST(rule_value AS UNSIGNED) FROM policy_rule WHERE rule_key='profit_target_monthly'),0) AS target_amount FROM v_profit_by_person p LEFT JOIN incentive_rate ir ON p.grade COLLATE utf8mb4_unicode_ci=ir.grade COLLATE utf8mb4_unicode_ci LEFT JOIN kpi_target kt ON p.staff_id=kt.staff_id AND p.sale_month=kt.target_month");
            $db->exec("CREATE OR REPLACE VIEW v_pipeline AS SELECT s.stage,s.sale_month,COUNT(*) AS deal_count,SUM(s.revenue) AS total_revenue,SUM(s.cost) AS total_cost,SUM(s.net_profit) AS total_net_profit FROM sales s GROUP BY s.stage,s.sale_month");
            $db->exec("CREATE OR REPLACE VIEW v_dashboard_summary AS SELECT SUM(revenue) AS total_revenue,SUM(net_profit) AS total_net_profit,COUNT(*) AS total_deals FROM sales WHERE stage IN('계약','촬영','방송완료')");
            $db->exec("CREATE OR REPLACE VIEW v_dashboard_ranking AS SELECT st.name,st.grade,st.team,st.staff_id,SUM(s.revenue) AS total_revenue,SUM(s.net_profit) AS total_net_profit,COUNT(*) AS total_deals FROM sales s JOIN staff st ON s.staff_id=st.staff_id WHERE s.stage IN('계약','촬영','방송완료') GROUP BY st.staff_id,st.name,st.grade,st.team");
            $db->exec("CREATE OR REPLACE VIEW v_dashboard_monthly AS SELECT s.sale_month,COUNT(CASE WHEN s.stage IN('계약','촬영','방송완료') THEN 1 END) AS deal_count,SUM(CASE WHEN s.stage IN('계약','촬영','방송완료') THEN s.revenue ELSE 0 END) AS total_revenue,SUM(CASE WHEN s.stage IN('계약','촬영','방송완료') THEN s.net_profit ELSE 0 END) AS total_net_profit,SUM(CASE WHEN s.stage='리드' THEN 1 ELSE 0 END) AS stage_lead,SUM(CASE WHEN s.stage='제안' THEN 1 ELSE 0 END) AS stage_proposal,SUM(CASE WHEN s.stage='계약' THEN 1 ELSE 0 END) AS stage_contract,SUM(CASE WHEN s.stage='촬영' THEN 1 ELSE 0 END) AS stage_filming,SUM(CASE WHEN s.stage='방송완료' THEN 1 ELSE 0 END) AS stage_complete FROM sales s GROUP BY s.sale_month ORDER BY s.sale_month");
            $db->exec("CREATE OR REPLACE VIEW v_performance_total AS SELECT ps.score_id,ps.staff_id,st.name,st.grade,st.team,ps.eval_month,ROUND(((ps.quant_revenue_rate+ps.quant_deal_rate)/2)*0.6,1) AS quant_score,ROUND((ps.qual_attitude+ps.qual_teamwork+ps.qual_contrib)/30*40,1) AS qual_score,ROUND(((ps.quant_revenue_rate+ps.quant_deal_rate)/2)*0.6+(ps.qual_attitude+ps.qual_teamwork+ps.qual_contrib)/30*40,1) AS total_score FROM performance_score ps JOIN staff st ON ps.staff_id=st.staff_id");
            $db->exec("CREATE OR REPLACE VIEW v_underperform_flag AS SELECT st.staff_id,st.name,st.grade,st.team,COUNT(DISTINCT p.sale_month) AS low_months FROM staff st LEFT JOIN v_profit_by_person p ON st.staff_id=p.staff_id AND p.deal_count<2 WHERE st.is_active=1 GROUP BY st.staff_id,st.name,st.grade,st.team HAVING low_months>=2");
            // audit_log action 컬럼 마이그레이션
            try { $db->exec("ALTER TABLE audit_log MODIFY COLUMN action VARCHAR(30) NOT NULL"); } catch(Exception $e3) {}
        } catch(Exception $e2) {}
    }
}
