<?php
define('IN_ADMIN', true);
include("../includes/common.php");
if($islogin==1){}else exit("<script language='javascript'>window.location.href='./login.php';</script>");
$act=isset($_GET['act'])?daddslashes($_GET['act']):null;
$admin_uid = 0;

if(!checkRefererHost())exit('{"code":403}');

@header('Content-Type: application/json; charset=UTF-8');

function build_update_api_url($url) {
	if (preg_match('/^https?:\/\//i', $url)) return $url;
	if (substr($url, 0, 2) === '..') {
		$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https://" : "http://";
		$current_dir_url = $protocol . $_SERVER['HTTP_HOST'] . dirname($_SERVER['REQUEST_URI']);
		return $current_dir_url . '/' . $url;
	}
	return $url;
}

function http_get($url, &$error = null, $timeout = 8) {
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
	curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
	curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
	curl_setopt($ch, CURLOPT_USERAGENT, 'CronLiteUpdateClient/1.0');
	$data = curl_exec($ch);
	$errno = curl_errno($ch);
	$err = curl_error($ch);
	$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	curl_close($ch);
	if ($data === false || $errno) {
		$error = $errno ? ($errno . ':' . $err) : 'curl_exec_failed';
		return false;
	}
	if ($httpcode >= 400) {
		$error = 'http_' . $httpcode;
		return false;
	}
	return $data;
}

switch($act){
case 'getcount':
	$thtime=date("Y-m-d").' 00:00:00';
	$lastday=date("Y-m-d",strtotime("-1 day")).' 00:00:00';
	$count1=$DB->getColumn("SELECT count(*) from pre_file");
	$count2=$DB->getColumn("SELECT count(*) from pre_file WHERE addtime>='$thtime'");
	$count3=$DB->getColumn("SELECT count(*) from pre_file WHERE addtime>='$lastday' AND addtime<'$thtime'");
	$count4=$DB->getColumn("SELECT count(*) from pre_user");

	$result=["code"=>0,"count1"=>$count1,"count2"=>$count2,"count3"=>$count3,"count4"=>$count4];
	exit(json_encode($result));
	break;
	case 'get_popup_notice':
		$update_url = build_update_api_url(UPDATE_API . '?act=popup_notice');
		$err = null;
		$res = http_get($update_url, $err, 8);
		if ($res === false) {
			exit(json_encode(['code'=>-1, 'msg'=>'弹窗公告请求失败']));
		}
		$data = json_decode($res, true);
		if (!$data || !isset($data['code'])) {
			exit(json_encode(['code'=>-1, 'msg'=>'弹窗公告返回异常']));
		}
		if($data['code'] == 0 && isset($data['data'])){
			$popup = $data['data'];
			$popup_id = 'popup_notice_' . $popup['id'];
			if(isset($_COOKIE[$popup_id])){
				exit(json_encode(['code'=>1, 'msg'=>'已隐藏']));
			}
			exit(json_encode([
				'code'=>0, 
				'data'=>[
					'id'=>$popup['id'],
					'title'=>$popup['title'],
					'content'=>$popup['content']
				]
			]));
		}
		exit(json_encode(['code'=>1, 'msg'=>'暂无弹窗公告']));
	break;
	case 'get_update_notice':
	$update_url = build_update_api_url(UPDATE_API . '?act=check&ver='.VERSION.'&url='.urlencode($_SERVER['HTTP_HOST']));
	$err = null;
	$res = http_get($update_url, $err, 8);
	if ($res === false) {
		exit(json_encode(['code'=>-1, 'msg'=>'公告接口请求失败']));
	}
	$data = json_decode($res, true);
	if (!$data || !isset($data['code'])) {
		exit(json_encode(['code'=>-1, 'msg'=>'公告接口返回异常']));
	}
	$msg = isset($data['msg']) ? $data['msg'] : '';
	$msg = preg_replace('/\d{4}[-\/]\d{1,2}[-\/]\d{1,2}(\s*\d{1,2}:\d{1,2}(:\d{1,2})?)?/i', '', $msg);
	$msg = preg_replace('/发布于[\s\d:年年月月日日]+/i', '', $msg);
	$msg = preg_replace('/更新时间?[\s\d:年年月月日日]+/i', '', $msg);
	$msg = preg_replace('/\[[\d\-:\s]+\]/', '', $msg);
	$msg = preg_replace('/\([\d\-:\s]+\)/', '', $msg);
	exit(json_encode(['code'=>0, 'msg'=>trim($msg)]));
break;
case 'set':
	if(isset($_POST['green_label_porn'])){
		$_POST['green_label_porn'] = implode(',',$_POST['green_label_porn']);
	}
	if(isset($_POST['green_label_terrorism'])){
		$_POST['green_label_terrorism'] = implode(',',$_POST['green_label_terrorism']);
	}
	foreach($_POST as $k=>$v){
		saveSetting($k, $v);
	}
	exit('{"code":0,"msg":"succ"}');
break;
case 'iptype':
	$result = [
	['name'=>'0_X_FORWARDED_FOR', 'ip'=>real_ip(0), 'city'=>get_ip_city(real_ip(0))],
	['name'=>'1_X_REAL_IP', 'ip'=>real_ip(1), 'city'=>get_ip_city(real_ip(1))],
	['name'=>'2_REMOTE_ADDR', 'ip'=>real_ip(2), 'city'=>get_ip_city(real_ip(2))]
	];
	exit(json_encode($result));
break;
case 'userList':
	$where = " 1=1";
	$params = [];
	if(isset($_POST['dstatus']) && $_POST['dstatus']>-1) {
		$dstatus = intval($_POST['dstatus']);
		$where .= " AND `enable`=:enable";
		$params[':enable'] = $dstatus;
	}
	if(isset($_POST['kw']) && !empty($_POST['kw'])) {
		$type = intval($_POST['type']);
		$kw = trim($_POST['kw']);
		if($type == 1){
			$where .= " AND `uid`=:kw_uid";
			$params[':kw_uid'] = $kw;
		}elseif($type == 2){
			$where .= " AND (`qq_openid`=:kw_openid OR `wx_openid`=:kw_openid)";
			$params[':kw_openid'] = $kw;
		}elseif($type == 3){
			$where .= " AND `nickname` LIKE :kw_like";
			$params[':kw_like'] = '%'.$kw.'%';
		}elseif($type == 4){
			$where .= " AND `regip`=:kw_regip";
			$params[':kw_regip'] = $kw;
		}elseif($type == 5){
			$where .= " AND `username`=:kw_username";
			$params[':kw_username'] = $kw;
		}
	}
	$offset = intval($_POST['offset']);
	$limit = intval($_POST['limit']);
	$total = $DB->getColumn("SELECT count(*) from pre_user WHERE{$where}", $params);
	$list = $DB->getAll("SELECT * FROM pre_user WHERE{$where} order by uid desc limit {$offset},{$limit}", $params);
	foreach($list as &$u){
		$storage_quota_mb = isset($conf['user_storage_quota']) ? $conf['user_storage_quota'] : null;
		$storage_total = ($storage_quota_mb !== null && $storage_quota_mb > 0) ? $storage_quota_mb * 1048576 : 0;
		$storage_total += isset($u['u_storage_quota']) ? intval($u['u_storage_quota']) : 0;
		$u['storage_total'] = $storage_total;
		$traffic_info = get_traffic_quota($u['uid']);
		$u['traffic_total'] = isset($traffic_info['quota_limit']) ? intval($traffic_info['quota_limit']) : 0;
	}
	exit(json_encode(['total'=>$total, 'rows'=>$list]));
	break;
case 'setUserEnable':
	$uid=intval($_POST['uid']);
	$enable=intval($_POST['enable']);
	$sql = "UPDATE pre_user SET enable='$enable' WHERE uid='$uid'";
	if($DB->exec($sql)!==false)exit('{"code":0,"msg":"修改用户成功！"}');
	else exit('{"code":-1,"msg":"修改用户失败['.$DB->error().']"}');
break;
case 'saveUserInfo':
	$uid=intval($_POST['uid']);
	$level=intval($_POST['level']);
	$sql = "UPDATE pre_user SET level='$level' WHERE uid='$uid'";
	if($DB->exec($sql)!==false)exit('{"code":0,"msg":"修改用户成功！"}');
	else exit('{"code":-1,"msg":"修改用户失败['.$DB->error().']"}');
break;
case 'saveUserQuota':
	$uid = intval($_POST['uid']);
	$storage_quota_mb = isset($_POST['storage_quota_mb']) ? floatval($_POST['storage_quota_mb']) : 0;
	$traffic_quota_mb = isset($_POST['traffic_quota_mb']) ? floatval($_POST['traffic_quota_mb']) : 0;
	$storage_bytes = $storage_quota_mb > 0 ? intval($storage_quota_mb * 1048576) : 0;
	$traffic_bytes = $traffic_quota_mb > 0 ? intval($traffic_quota_mb * 1048576) : 0;
	$cols = $DB->getAll("SHOW COLUMNS FROM pre_user");
	$fields = [];
	foreach($cols as $c){ $fields[] = $c['Field']; }
	if(!in_array('u_storage_quota', $fields)){
		$DB->exec("ALTER TABLE pre_user ADD `u_storage_quota` bigint(20) unsigned NOT NULL DEFAULT '0' AFTER `traffic_used`");
	}
	if(!in_array('u_traffic_quota', $fields)){
		$DB->exec("ALTER TABLE pre_user ADD `u_traffic_quota` bigint(20) unsigned NOT NULL DEFAULT '0' AFTER `u_storage_quota`");
	}
	$sql = "UPDATE pre_user SET u_storage_quota=:s, u_traffic_quota=:t WHERE uid=:uid";
	$result = $DB->exec($sql, [':s'=>$storage_bytes, ':t'=>$traffic_bytes, ':uid'=>$uid]);
	if($result!==false) exit('{"code":0,"msg":"用户配额已更新"}');
	else exit('{"code":-1,"msg":"更新失败['.$DB->error().']"}');
break;
case 'delUser':
	$uid=intval($_POST['uid']);
	$row=$DB->getRow("select * from pre_user where uid='$uid' limit 1");
	if(!$row)
		exit('{"code":-1,"msg":"当前用户不存在！"}');
	$sql = "DELETE FROM pre_user WHERE uid='$uid'";
	if($DB->exec($sql))exit('{"code":0,"msg":"删除用户成功！"}');
	else exit('{"code":-1,"msg":"删除用户失败['.$DB->error().']"}');
break;

case 'create_card':
	if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}');
	if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');
	
	$card_type = isset($_POST['card_type']) ? $_POST['card_type'] : 'storage';
	$storage_quota = isset($_POST['storage_quota']) ? intval($_POST['storage_quota']) : 0;
	$traffic_quota = isset($_POST['traffic_quota']) ? intval($_POST['traffic_quota']) : 0;
	$expire_days = isset($_POST['expire_days']) ? intval($_POST['expire_days']) : 0;
	$remark = isset($_POST['remark']) ? trim(htmlspecialchars($_POST['remark'])) : null;
	$count = isset($_POST['count']) ? intval($_POST['count']) : 1;
	
	if($count < 1 || $count > 100) {
		exit('{"code":-1,"msg":"生成数量必须在1-100之间"}');
	}
	
	if($card_type == 'storage' && $storage_quota <= 0) {
		exit('{"code":-1,"msg":"存储空间卡密必须设置存储配额"}');
	}
	
	if($card_type == 'traffic' && $traffic_quota <= 0) {
		exit('{"code":-1,"msg":"流量配额卡密必须设置流量配额"}');
	}
	
	if($card_type == 'both' && ($storage_quota <= 0 || $traffic_quota <= 0)) {
		exit('{"code":-1,"msg":"存储+流量卡密必须同时设置存储和流量配额"}');
	}
	
	$success_count = 0;
	for($i = 0; $i < $count; $i++) {
		$card_id = create_card($card_type, $storage_quota, $traffic_quota, $expire_days, $remark, $uid);
		if($card_id) {
			$success_count++;
		}
	}
	
	exit('{"code":0,"msg":"成功生成'.$success_count.'张卡密"}');
break;

case 'disable_card':
	if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}');
	if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');
	
	$card_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
	if($card_id <= 0) {
		exit('{"code":-1,"msg":"卡密ID错误"}');
	}
	
	$result = disable_card($card_id, $uid);
	if($result) {
		exit('{"code":0,"msg":"禁用成功"}');
	} else {
		exit('{"code":-1,"msg":"禁用失败"}');
	}
break;

case 'enable_card':
	if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}');
	if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');
	
	$card_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
	if($card_id <= 0) {
		exit('{"code":-1,"msg":"卡密ID错误"}');
	}
	
	$result = $DB->exec("UPDATE `pre_card` SET `status`=0 WHERE `id`=:id", [':id'=>$card_id]);
	if($result) {
		exit('{"code":0,"msg":"启用成功"}');
	} else {
		exit('{"code":-1,"msg":"启用失败"}');
	}
break;

case 'delete_card':
	if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}');
	if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');
	
	$card_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
	if($card_id <= 0) {
		exit('{"code":-1,"msg":"卡密ID错误"}');
	}
	
	$result = delete_card($card_id);
	if($result) {
		exit('{"code":0,"msg":"删除成功"}');
	} else {
		exit('{"code":-1,"msg":"删除失败"}');
	}
break;

case 'delete_used_cards':
	if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}');
	if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');
	$result = $DB->exec("DELETE FROM pre_card WHERE status=1");
	if($result!==false) exit(json_encode(['code'=>0,'msg'=>'已删除已使用卡密']));
	else exit(json_encode(['code'=>-1,'msg'=>'删除失败']));
break;

case 'export_unused_cards':
	if(!$islogin)exit;
	$card_type = isset($_GET['card_type']) ? trim($_GET['card_type']) : '';
	$filename = 'unused_cards'.($card_type ? '_'.$card_type : '').'.txt';
	header('Content-Type: text/plain; charset=UTF-8');
	header('Content-Disposition: attachment; filename="'.$filename.'"');
	if($card_type){
		$rows = $DB->getAll("SELECT card_code FROM pre_card WHERE status=0 AND card_type=:type ORDER BY created_at DESC", [':type'=>$card_type]);
	}else{
		$rows = $DB->getAll("SELECT card_code FROM pre_card WHERE status=0 ORDER BY created_at DESC");
	}
	foreach($rows as $r){
		echo $r['card_code']."\n";
	}
	exit;
break;
case 'export_unused_cards_txt':
	if(!$islogin)exit;
	$card_type = isset($_GET['card_type']) ? trim($_GET['card_type']) : '';
	$filename = 'unused_cards'.($card_type ? '_'.$card_type : '').'.txt';
	header('Content-Type: text/plain; charset=UTF-8');
	header('Content-Disposition: attachment; filename="'.$filename.'"');
	if($card_type){
		$rows = $DB->getAll("SELECT card_code FROM pre_card WHERE status=0 AND card_type=:type ORDER BY created_at DESC", [':type'=>$card_type]);
	}else{
		$rows = $DB->getAll("SELECT card_code FROM pre_card WHERE status=0 ORDER BY created_at DESC");
	}
	foreach($rows as $r){
		echo $r['card_code']."\n";
	}
	exit;
break;

case 'card_statistics':
	$total = $DB->getColumn("SELECT COUNT(*) FROM pre_card");
	$unused = $DB->getColumn("SELECT COUNT(*) FROM pre_card WHERE status=0");
	$used = $DB->getColumn("SELECT COUNT(*) FROM pre_card WHERE status=1");
	$disabled = $DB->getColumn("SELECT COUNT(*) FROM pre_card WHERE status=2");
	
	exit(json_encode([
		'code' => 0,
		'total' => $total,
		'unused' => $unused,
		'used' => $used,
		'disabled' => $disabled
	]));
break;

case 'test_traffic_log':
	if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');

	$file_id = intval($_POST['file_id']);
	$file_hash = trim($_POST['file_hash']);
	$file_name = trim($_POST['file_name']);
	$file_size = intval($_POST['file_size']);
	$file_type = trim($_POST['file_type']);
	$owner_uid = intval($_POST['owner_uid']);
	$visitor_uid = intval($_POST['visitor_uid']);
	$visitor_ip = trim($_POST['visitor_ip']);
	$access_type = trim($_POST['access_type']);
	$traffic_size = intval($_POST['traffic_size']);

	$result = log_traffic_usage($file_id, $file_hash, $file_name, $file_size, $file_type, $owner_uid, $visitor_uid, $visitor_ip, $access_type, $traffic_size);
	if($result) {
		exit('{"code":0,"msg":"测试记录插入成功"}');
	} else {
		exit('{"code":-1,"msg":"插入失败"}');
	}
break;

	case 'clear_test_traffic':
		if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');

		$sql = "DELETE FROM pre_traffic_log WHERE file_hash LIKE 'test_hash_%'";
		$result = $DB->exec($sql);
		exit('{"code":0,"msg":"已删除'.$result.'条测试记录"}');
	break;

	case 'get_traffic_stats':
		if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');

		$start_date = isset($_GET['start_date']) ? $_GET['start_date'] . ' 00:00:00' : date('Y-m-d 00:00:00');
		$end_date = isset($_GET['end_date']) ? $_GET['end_date'] . ' 23:59:59' : date('Y-m-d 23:59:59');

		$stats = get_traffic_statistics($start_date, $end_date);

		$guest_today = $DB->getRow("SELECT SUM(traffic_size) as total FROM pre_traffic_log WHERE visitor_uid=0 AND created_at >= '".date('Y-m-d 00:00:00')."'");
		$guest_today_traffic = $guest_today && $guest_today['total'] ? $guest_today['total'] : 0;
		$guest_ratio = $stats && $stats['total_traffic'] > 0 ? round(($guest_today_traffic / $stats['total_traffic']) * 100, 2) : 0;

		$anomaly_logs = $DB->getAll("SELECT visitor_uid, visitor_ip, COUNT(*) as cnt, SUM(traffic_size) as total FROM pre_traffic_log WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR) GROUP BY visitor_uid, visitor_ip HAVING cnt > 50 OR total > 104857600");
		$anomaly_count = count($anomaly_logs);

		$download_stats = $DB->getRow("SELECT SUM(traffic_size) as total FROM pre_traffic_log WHERE access_type='download' AND created_at >= '".date('Y-m-d 00:00:00', time()-86400*7)."'");
		$preview_stats = $DB->getRow("SELECT SUM(traffic_size) as total FROM pre_traffic_log WHERE access_type='preview' AND created_at >= '".date('Y-m-d 00:00:00', time()-86400*7)."'");
		$download_total = $download_stats && $download_stats['total'] ? $download_stats['total'] : 0;
		$preview_total = $preview_stats && $preview_stats['total'] ? $preview_stats['total'] : 0;

		$week_data = [];
		for($i=6; $i>=0; $i--){
			$date = date('Y-m-d', time()-86400*$i);
			$day_stats = get_traffic_statistics($date.' 00:00:00', $date.' 23:59:59');
			$week_data[] = [
				'date' => $date,
				'traffic' => $day_stats ? $day_stats['total_traffic'] : 0
			];
		}

		exit(json_encode([
			'code' => 0,
			'stats' => $stats,
			'guest_ratio' => $guest_ratio,
			'anomaly_count' => $anomaly_count,
			'download_total' => $download_total,
			'preview_total' => $preview_total,
			'week_data' => $week_data
		]));
	break;

case 'check_traffic_reset_status':
	if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}');
	if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');
	
	$reset_interval = isset($conf['traffic_quota_reset_interval']) ? intval($conf['traffic_quota_reset_interval']) : 30;
	$total_users = $DB->getColumn("SELECT COUNT(*) FROM pre_user");
	$users_with_reset = $DB->getColumn("SELECT COUNT(*) FROM pre_user WHERE last_traffic_reset_time IS NOT NULL");
	$last_reset = $DB->getRow("SELECT last_traffic_reset_time FROM pre_user WHERE last_traffic_reset_time IS NOT NULL ORDER BY last_traffic_reset_time DESC LIMIT 1");
	
	$issues = [];
	
	// 检查配置问题
	if($reset_interval <= 0) {
		$issues[] = "流量自动重置已禁用";
	} elseif($reset_interval < 1) {
		$issues[] = "重置周期设置异常：" . $reset_interval . "天";
	}
	
	// 检查用户状态问题
	if($total_users > 0 && $users_with_reset == 0) {
		$issues[] = "所有用户都没有重置时间基准";
	} elseif($users_with_reset < $total_users) {
		$issues[] = "有 " . ($total_users - $users_with_reset) . " 个用户缺少重置时间基准";
	}
	
	// 检查最近重置
	if($last_reset && $reset_interval > 0) {
		$days_since_reset = (time() - strtotime($last_reset['last_traffic_reset_time'])) / 86400;
		if($days_since_reset < 1) {
			$issues[] = "检测到最近24小时内有重置操作，可能存在异常";
		}
	}
	
	$result = [
		'code' => 0,
		'config' => [
			'interval' => $reset_interval
		],
		'stats' => [
			'total_users' => $total_users,
			'users_with_reset' => $users_with_reset,
			'last_reset' => $last_reset ? $last_reset['last_traffic_reset_time'] : '无'
		],
		'issues' => $issues
	];
	
	exit(json_encode($result));
break;

case 'fix_traffic_reset':
	if(!$_POST['csrf_token'] || $_POST['csrf_token']!=$_SESSION['csrf_token'])exit('{"code":-1,"msg":"CSRF TOKEN ERROR"}');
	if(!$islogin)exit('{"code":-1,"msg":"请先登录"}');
	
	$fixed_count = 0;
	$reset_interval = isset($conf['traffic_quota_reset_interval']) ? intval($conf['traffic_quota_reset_interval']) : 30;
	
	if($reset_interval > 0) {
		// 为没有重置时间的用户设置基准时间
		$result = $DB->exec("UPDATE pre_user SET last_traffic_reset_time = NOW() WHERE last_traffic_reset_time IS NULL");
		$fixed_count = $result !== false ? $result : 0;
	}
	
	exit(json_encode(['code'=>0, 'msg'=>'修复完成', 'fixed_count'=>$fixed_count]));
break;

	default:
		exit('{"code":-4,"msg":"No Act"}');
	break;
}
