<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Teenup_model extends CI_Model
{
	public function fetch_collection($name)
	{
		try {
			$rows = $this->common->readdatafromcollectionwhereclause($name);
			return is_array($rows) ? $rows : array();
		} catch (\Throwable $e) {
			return array();
		}
	}

	public function app_users()
	{
		$list = array();
		foreach ($this->fetch_collection('users') as $id => $data) {
			$u = $this->normalize_user($id, is_array($data) ? $data : array());
			if (!empty($u['isAdmin'])) {
				continue;
			}
			$list[] = $u;
		}
		usort($list, function ($a, $b) {
			return (int) $b['createdAt'] <=> (int) $a['createdAt'];
		});
		return $list;
	}

	public function load_user($uid)
	{
		$uid = trim((string) $uid);
		if ($uid === '') {
			return null;
		}
		try {
			$row = $this->common->readdatadocument('users', $uid);
		} catch (\Throwable $e) {
			$row = '';
		}
		if (!is_array($row) || empty($row)) {
			return null;
		}
		return $this->normalize_user($uid, $row);
	}

	public function posts()
	{
		$list = array();
		foreach ($this->fetch_collection('posts') as $id => $data) {
			$list[] = $this->normalize_post($id, is_array($data) ? $data : array());
		}
		$reportCounts = array();
		foreach ($this->reports() as $r) {
			$cid = isset($r['contentId']) ? (string) $r['contentId'] : '';
			if ($cid === '') {
				continue;
			}
			if (!isset($reportCounts[$cid])) {
				$reportCounts[$cid] = 0;
			}
			$reportCounts[$cid]++;
		}
		foreach ($list as &$p) {
			$n = 0;
			if (isset($reportCounts[$p['id']])) {
				$n += (int) $reportCounts[$p['id']];
			}
			if ($p['docId'] !== $p['id'] && isset($reportCounts[$p['docId']])) {
				$n += (int) $reportCounts[$p['docId']];
			}
			$p['reportsCount'] = $n;
		}
		unset($p);
		usort($list, function ($a, $b) {
			return (int) $b['createdAt'] <=> (int) $a['createdAt'];
		});
		return $list;
	}

	public function load_post($id)
	{
		$id = trim((string) $id);
		if ($id === '') {
			return null;
		}
		try {
			$row = $this->common->readdatadocument('posts', $id);
		} catch (\Throwable $e) {
			$row = '';
		}
		if (is_array($row) && !empty($row)) {
			return $this->normalize_post($id, $row);
		}
		foreach ($this->posts() as $p) {
			if ($p['docId'] === $id || $p['id'] === $id) {
				return $p;
			}
		}
		return null;
	}

	public function update_post($id, $payload)
	{
		$id = trim((string) $id);
		if ($id === '') {
			return false;
		}
		$found = $this->load_post($id);
		$docId = $found ? $found['docId'] : $id;
		$clean = array();
		foreach ((array) $payload as $k => $v) {
			if ($v === null) {
				continue;
			}
			$clean[$k] = $v;
		}
		$clean['updatedAt'] = (int) round(microtime(true) * 1000);
		return $this->common->updatedatamodel('posts', $docId, $clean);
	}

	public function allowed_post_statuses()
	{
		return array('visible', 'hidden', 'removed');
	}

	public function posts_for_user($uid)
	{
		$uid = trim((string) $uid);
		$out = array();
		foreach ($this->posts() as $p) {
			if ($p['authorUid'] === $uid) {
				$out[] = $p;
			}
		}
		return $out;
	}

	public function comments_for_post($post)
	{
		$ids = array();
		if (is_array($post)) {
			if (!empty($post['docId'])) {
				$ids[] = (string) $post['docId'];
			}
			if (!empty($post['id']) && !in_array((string) $post['id'], $ids, true)) {
				$ids[] = (string) $post['id'];
			}
		} else {
			$ids[] = trim((string) $post);
		}
		$list = array();
		$seen = array();
		foreach ($ids as $id) {
			foreach (array('comments', 'Comments') as $sub) {
				try {
					$rows = $this->common->readdatacollection('posts', $sub, $id);
				} catch (\Throwable $e) {
					$rows = array();
				}
				foreach ((array) $rows as $cid => $data) {
					if (isset($seen[$cid])) {
						continue;
					}
					$seen[$cid] = true;
					$list[] = $this->normalize_comment($cid, is_array($data) ? $data : array());
				}
			}
		}
		if (empty($list)) {
			foreach (array('comments', 'Comments') as $col) {
				foreach ($this->fetch_collection($col) as $cid => $data) {
					$d = is_array($data) ? $data : array();
					$pid = '';
					foreach (array('postId', 'postID', 'post_id', 'contentId', 'postUid') as $k) {
						if (!empty($d[$k])) {
							$pid = (string) $d[$k];
							break;
						}
					}
					if ($pid === '' || !in_array($pid, $ids, true)) {
						continue;
					}
					if (isset($seen[$cid])) {
						continue;
					}
					$seen[$cid] = true;
					$list[] = $this->normalize_comment($cid, $d);
				}
				if (!empty($list)) {
					break;
				}
			}
		}
		usort($list, function ($a, $b) {
			return (int) $b['createdAt'] <=> (int) $a['createdAt'];
		});
		return $list;
	}

	private function normalize_comment($id, $d)
	{
		$createdAt = $this->to_millis(isset($d['createdAt']) ? $d['createdAt'] : (isset($d['timestamp']) ? $d['timestamp'] : 0));
		$text = '';
		foreach (array('text', 'comment', 'message', 'body', 'caption') as $k) {
			if (!empty($d[$k]) && is_string($d[$k])) {
				$text = (string) $d[$k];
				break;
			}
		}
		$author = '';
		foreach (array('authorName', 'userName', 'username', 'displayName', 'name') as $k) {
			if (!empty($d[$k]) && is_string($d[$k])) {
				$author = (string) $d[$k];
				break;
			}
		}
		$authorUid = isset($d['authorUid']) ? (string) $d['authorUid'] : (isset($d['userId']) ? (string) $d['userId'] : (isset($d['uid']) ? (string) $d['uid'] : ''));
		return array(
			'id' => (string) $id,
			'text' => $text,
			'authorName' => $author,
			'authorUid' => $authorUid,
			'createdAt' => $createdAt,
			'createdAtLabel' => $this->fmt_dt($createdAt),
		);
	}

	public function stories()
	{
		$list = array();
		foreach ($this->fetch_collection('stories') as $id => $data) {
			$d = is_array($data) ? $data : array();
			$createdAt = $this->to_millis(isset($d['createdAt']) ? $d['createdAt'] : (isset($d['timestamp']) ? $d['timestamp'] : 0));
			$list[] = array(
				'id' => isset($d['id']) ? (string) $d['id'] : (string) $id,
				'authorUid' => isset($d['authorUid']) ? (string) $d['authorUid'] : '',
				'authorName' => isset($d['authorName']) ? (string) $d['authorName'] : '',
				'imageUrl' => isset($d['imageUrl']) ? (string) $d['imageUrl'] : '',
				'createdAt' => $createdAt,
				'createdAtLabel' => $this->fmt_dt($createdAt),
			);
		}
		usort($list, function ($a, $b) {
			return (int) $b['createdAt'] <=> (int) $a['createdAt'];
		});
		return $list;
	}

	public function reports()
	{
		$list = array();
		foreach (array('reports', 'Reports') as $col) {
			$rows = $this->fetch_collection($col);
			if (empty($rows)) {
				continue;
			}
			foreach ($rows as $id => $data) {
				$list[] = $this->normalize_report($id, is_array($data) ? $data : array());
			}
			break;
		}
		usort($list, function ($a, $b) {
			return (int) $b['createdAt'] <=> (int) $a['createdAt'];
		});
		return $list;
	}

	public function activities()
	{
		$list = array();
		foreach (array('activities', 'Activities') as $col) {
			$rows = $this->fetch_collection($col);
			if (empty($rows)) {
				continue;
			}
			foreach ($rows as $id => $data) {
				$d = is_array($data) ? $data : array();
				$createdAt = $this->to_millis(isset($d['createdAt']) ? $d['createdAt'] : 0);
				$status = isset($d['status']) ? strtolower((string) $d['status']) : 'pending';
				$list[] = array(
					'id' => (string) $id,
					'name' => isset($d['name']) ? (string) $d['name'] : (isset($d['title']) ? (string) $d['title'] : ''),
					'status' => $status,
					'createdAt' => $createdAt,
				);
			}
			break;
		}
		return $list;
	}

	public function groups()
	{
		$list = array();
		foreach (array('groups', 'Groups') as $col) {
			$rows = $this->fetch_collection($col);
			if (empty($rows)) {
				continue;
			}
			foreach ($rows as $id => $data) {
				$d = is_array($data) ? $data : array();
				$list[] = array(
					'id' => (string) $id,
					'name' => isset($d['name']) ? (string) $d['name'] : (isset($d['groupName']) ? (string) $d['groupName'] : ''),
					'status' => isset($d['status']) ? strtolower((string) $d['status']) : 'active',
				);
			}
			break;
		}
		if (!empty($list)) {
			return $list;
		}
		foreach ($this->fetch_collection('conversations') as $id => $data) {
			$d = is_array($data) ? $data : array();
			$type = strtolower(trim((string) (isset($d['chatType']) ? $d['chatType'] : '')));
			if ($type !== 'group') {
				continue;
			}
			$list[] = array(
				'id' => (string) $id,
				'name' => isset($d['groupName']) ? (string) $d['groupName'] : '',
				'status' => 'active',
			);
		}
		return $list;
	}

	public function subscriptions()
	{
		$list = array();
		foreach (array('subscriptions', 'Subscriptions') as $col) {
			$rows = $this->fetch_collection($col);
			if (empty($rows)) {
				continue;
			}
			foreach ($rows as $id => $data) {
				$list[] = $this->normalize_subscription($id, is_array($data) ? $data : array());
			}
			break;
		}
		return $list;
	}

	public function transactions()
	{
		$list = array();
		foreach (array('transactions', 'Transactions', 'payments') as $col) {
			if ($col === 'payments') {
				continue;
			}
			$rows = $this->fetch_collection($col);
			if (empty($rows)) {
				continue;
			}
			foreach ($rows as $id => $data) {
				$list[] = $this->normalize_transaction($id, is_array($data) ? $data : array());
			}
			break;
		}
		usort($list, function ($a, $b) {
			return (int) $b['createdAt'] <=> (int) $a['createdAt'];
		});
		return $list;
	}

	public function audit_logs($limit = 20, $targetId = '')
	{
		$list = array();
		foreach ($this->fetch_collection('audit_logs') as $id => $data) {
			$d = is_array($data) ? $data : array();
			$tid = isset($d['targetId']) ? (string) $d['targetId'] : '';
			if ($targetId !== '' && $tid !== $targetId) {
				continue;
			}
			$createdAt = $this->to_millis(isset($d['createdAt']) ? $d['createdAt'] : 0);
			$list[] = array(
				'id' => (string) $id,
				'adminName' => isset($d['adminName']) ? (string) $d['adminName'] : 'Admin',
				'action' => isset($d['action']) ? (string) $d['action'] : '',
				'targetType' => isset($d['targetType']) ? (string) $d['targetType'] : '',
				'targetId' => $tid,
				'targetLabel' => isset($d['targetLabel']) ? (string) $d['targetLabel'] : '',
				'description' => isset($d['description']) ? (string) $d['description'] : '',
				'createdAt' => $createdAt,
				'createdAtLabel' => $this->fmt_dt($createdAt),
			);
		}
		usort($list, function ($a, $b) {
			return (int) $b['createdAt'] <=> (int) $a['createdAt'];
		});
		if ($limit > 0) {
			$list = array_slice($list, 0, (int) $limit);
		}
		return $list;
	}

	public function log_audit($action, $targetType, $targetId, $description = '', $targetLabel = '')
	{
		$session = $this->session->userdata('session_data');
		$adminId = is_array($session) && isset($session['userId']) ? (string) $session['userId'] : '';
		$adminName = is_array($session) && isset($session['name']) ? (string) $session['name'] : 'Admin';
		$ip = '';
		if (isset($this->input)) {
			$ip = (string) $this->input->ip_address();
		}
		$now = (int) round(microtime(true) * 1000);
		$docId = 'log_' . $now . '_' . substr(md5(uniqid((string) mt_rand(), true)), 0, 6);
		try {
			$this->common->adddatamodel('audit_logs', $docId, array(
				'adminId' => $adminId,
				'adminName' => $adminName,
				'action' => (string) $action,
				'targetType' => (string) $targetType,
				'targetId' => (string) $targetId,
				'targetLabel' => (string) $targetLabel,
				'description' => (string) $description,
				'createdAt' => $now,
				'ip' => $ip,
			));
		} catch (\Throwable $e) {
			// audit failure should not block the admin action
		}
	}

	public function update_user($uid, $payload)
	{
		$uid = trim((string) $uid);
		if ($uid === '') {
			return false;
		}
		$clean = array();
		foreach ((array) $payload as $k => $v) {
			if ($v === null) {
				continue;
			}
			$clean[$k] = $v;
		}
		$clean['updatedAt'] = (int) round(microtime(true) * 1000);
		return $this->common->updatedatamodel('users', $uid, $clean);
	}

	public function dashboard()
	{
		$out = array(
			'load_error' => '',
			'kpis' => array(),
			'charts' => array(),
			'recent_users' => array(),
			'recent_reports' => array(),
			'recent_moderation' => array(),
			'recent_transactions' => array(),
			'collection_notes' => array(),
		);

		try {
			$users = $this->app_users();
			$posts = $this->posts();
			$stories = $this->stories();
			$reports = $this->reports();
			$groups = $this->groups();
			$activities = $this->activities();
			$subs = $this->subscriptions();
			$tx = $this->transactions();
			$audit = $this->audit_logs(8);
		} catch (\Throwable $e) {
			$out['load_error'] = $e->getMessage();
			$users = $posts = $stories = $reports = $groups = $activities = $subs = $tx = $audit = array();
		}

		$now = (int) round(microtime(true) * 1000);
		$seven = 7 * 24 * 60 * 60 * 1000;
		$newUsers = 0;
		$activeUsers = 0;
		$suspendedUsers = 0;
		foreach ($users as $u) {
			if ($u['createdAt'] >= ($now - $seven)) {
				$newUsers++;
			}
			if ($u['status'] === 'active') {
				$activeUsers++;
			}
			if ($u['status'] === 'suspended') {
				$suspendedUsers++;
			}
		}

		$photoCount = 0;
		foreach ($posts as $p) {
			$photoCount += count($p['images']);
			if (count($p['images']) === 0 && strtolower($p['category']) === 'photo') {
				$photoCount++;
			}
		}
		$photoCount += count($stories);

		$pendingReports = 0;
		$pendingModeration = 0;
		foreach ($reports as $r) {
			if ($r['status'] === 'pending') {
				$pendingReports++;
			}
			if (in_array($r['status'], array('pending', 'under_review'), true)) {
				$pendingModeration++;
			}
		}

		$activeSubs = 0;
		$revenueCents = 0;
		foreach ($subs as $s) {
			if ($s['status'] === 'active') {
				$activeSubs++;
			}
		}
		foreach ($tx as $t) {
			if ($t['status'] === 'successful') {
				$revenueCents += (int) $t['amountCents'];
			}
		}

		$notes = array();
		if (empty($reports)) {
			$notes[] = 'reports';
		}
		if (empty($activities)) {
			$notes[] = 'activities';
		}
		if (empty($subs)) {
			$notes[] = 'subscriptions';
		}
		if (empty($tx)) {
			$notes[] = 'transactions';
		}

		$out['kpis'] = array(
			'total_users' => count($users),
			'active_users' => $activeUsers,
			'new_users' => $newUsers,
			'total_posts' => count($posts),
			'total_photos' => $photoCount,
			'total_groups' => count($groups),
			'total_activities' => count($activities),
			'pending_reports' => $pendingReports,
			'pending_moderation' => $pendingModeration,
			'suspended_users' => $suspendedUsers,
			'active_subscriptions' => $activeSubs,
			'total_revenue' => $this->fmt_money($revenueCents),
			'total_revenue_cents' => $revenueCents,
		);
		$out['charts'] = array(
			'registration' => $this->series_by_day($users, 'createdAt', 14),
			'active_age' => $this->count_by_key($users, 'ageGroup', 'active'),
			'posts' => $this->series_by_day($posts, 'createdAt', 14),
			'posts_topics' => $this->count_topics($posts),
			'reports' => $this->count_by_key($reports, 'status'),
			'revenue' => $this->revenue_by_day($tx, 14),
		);
		$out['recent_users'] = array_slice($users, 0, 6);
		$out['recent_reports'] = array_slice($reports, 0, 6);
		$out['recent_moderation'] = $audit;
		$out['recent_transactions'] = array_slice($tx, 0, 6);
		$out['collection_notes'] = $notes;
		return $out;
	}

	public function normalize_user($docId, $d)
	{
		$role = strtolower(trim((string) (isset($d['userRole']) ? $d['userRole'] : (isset($d['role']) ? $d['role'] : ''))));
		$isAdmin = ($role === 'admin' || $role === 'super_admin');
		$createdAt = $this->to_millis(isset($d['createdAt']) ? $d['createdAt'] : 0);
		$updatedAt = $this->to_millis(isset($d['updatedAt']) ? $d['updatedAt'] : 0);
		$displayName = isset($d['displayName']) ? trim((string) $d['displayName']) : '';
		if ($displayName === '' && !empty($d['name'])) {
			$displayName = trim((string) $d['name']);
		}
		$username = isset($d['username']) ? trim((string) $d['username']) : '';
		if ($username === '' && !empty($d['userName'])) {
			$username = trim((string) $d['userName']);
		}
		$photoUrl = isset($d['photoUrl']) ? (string) $d['photoUrl'] : '';
		if ($photoUrl === '' && !empty($d['image'])) {
			$photoUrl = (string) $d['image'];
		}
		$interests = $this->string_list(isset($d['interests']) ? $d['interests'] : array());
		$lookingFor = $this->string_list(isset($d['lookingFor']) ? $d['lookingFor'] : array());
		$city = isset($d['city']) ? trim((string) $d['city']) : '';
		$country = isset($d['country']) ? trim((string) $d['country']) : '';
		$locationLabel = trim($city . (($city !== '' && $country !== '') ? ', ' : '') . $country);
		if ($locationLabel === '') {
			$locationLabel = !empty($d['isLocationEnabled']) ? 'Approximate area on' : '—';
		}
		$subStatus = '';
		if (isset($d['subscriptionStatus'])) {
			$subStatus = (string) $d['subscriptionStatus'];
		} elseif (isset($d['subscription']) && is_string($d['subscription'])) {
			$subStatus = (string) $d['subscription'];
		} elseif (isset($d['subscription']) && is_array($d['subscription']) && isset($d['subscription']['status'])) {
			$subStatus = (string) $d['subscription']['status'];
		}
		$subLabel = $subStatus !== '' ? $subStatus : 'Free';
		$status = $this->resolve_user_status($d);

		return array(
			'uid' => isset($d['uid']) && (string) $d['uid'] !== '' ? (string) $d['uid'] : (string) $docId,
			'docId' => (string) $docId,
			'displayName' => $displayName,
			'username' => $username,
			'email' => isset($d['email']) ? (string) $d['email'] : '',
			'bio' => isset($d['bio']) ? (string) $d['bio'] : '',
			'ageGroup' => isset($d['ageGroup']) ? (string) $d['ageGroup'] : '',
			'photoUrl' => $photoUrl,
			'interests' => $interests,
			'lookingFor' => $lookingFor,
			'isLocationEnabled' => !empty($d['isLocationEnabled']),
			'locationLabel' => $locationLabel,
			'postsCount' => isset($d['postsCount']) ? (int) $d['postsCount'] : 0,
			'photosCount' => isset($d['photosCount']) ? (int) $d['photosCount'] : 0,
			'activitiesCount' => isset($d['activitiesCount']) ? (int) $d['activitiesCount'] : 0,
			'connectionsCount' => isset($d['connectionsCount']) ? (int) $d['connectionsCount'] : 0,
			'status' => $status,
			'subscription' => $subLabel,
			'userRole' => $role !== '' ? $role : 'user',
			'isAdmin' => $isAdmin,
			'createdAt' => $createdAt,
			'updatedAt' => $updatedAt,
			'createdAtLabel' => $this->fmt_dt($createdAt),
			'updatedAtLabel' => $this->fmt_dt($updatedAt),
		);
	}

	public function normalize_post($docId, $d)
	{
		$createdAt = $this->to_millis(isset($d['createdAt']) ? $d['createdAt'] : 0);
		$images = array();
		if (isset($d['images']) && is_array($d['images'])) {
			foreach ($d['images'] as $img) {
				if (is_string($img) && $img !== '') {
					$images[] = $img;
				}
			}
		}
		$loc = (isset($d['location']) && is_array($d['location'])) ? $d['location'] : array();
		$city = isset($loc['city']) ? trim((string) $loc['city']) : '';
		$area = isset($loc['address']) ? trim((string) $loc['address']) : '';
		$place = $area !== '' ? $area : $city;
		$status = $this->resolve_post_status($d);
		$category = isset($d['category']) ? (string) $d['category'] : '';
		return array(
			'id' => isset($d['id']) && (string) $d['id'] !== '' ? (string) $d['id'] : (string) $docId,
			'docId' => (string) $docId,
			'authorUid' => isset($d['authorUid']) ? (string) $d['authorUid'] : '',
			'authorName' => isset($d['authorName']) ? (string) $d['authorName'] : '',
			'authorAvatar' => isset($d['authorAvatar']) ? (string) $d['authorAvatar'] : '',
			'caption' => isset($d['caption']) ? (string) $d['caption'] : '',
			'category' => $category,
			'contentType' => $category !== '' ? $category : 'Post',
			'privacy' => isset($d['privacy']) ? (string) $d['privacy'] : '',
			'images' => $images,
			'preview' => !empty($images) ? $images[0] : '',
			'topics' => $this->string_list(isset($d['topics']) ? $d['topics'] : array()),
			'likesCount' => isset($d['likesCount']) ? (int) $d['likesCount'] : 0,
			'commentsCount' => isset($d['commentsCount']) ? (int) $d['commentsCount'] : 0,
			'reportsCount' => isset($d['reportsCount']) ? (int) $d['reportsCount'] : 0,
			'status' => $status,
			'placeLabel' => $place !== '' ? $place : '—',
			'createdAt' => $createdAt,
			'createdAtLabel' => $this->fmt_dt($createdAt),
		);
	}

	private function normalize_report($id, $d)
	{
		$createdAt = $this->to_millis(isset($d['createdAt']) ? $d['createdAt'] : (isset($d['date']) ? $d['date'] : 0));
		$status = strtolower(trim((string) (isset($d['status']) ? $d['status'] : 'pending')));
		$status = str_replace(' ', '_', $status);
		$priority = strtolower(trim((string) (isset($d['priority']) ? $d['priority'] : 'medium')));
		return array(
			'id' => (string) $id,
			'contentId' => isset($d['contentId']) ? (string) $d['contentId'] : (isset($d['postId']) ? (string) $d['postId'] : ''),
			'reporter' => isset($d['reporterName']) ? (string) $d['reporterName'] : (isset($d['reporter']) ? (string) $d['reporter'] : ''),
			'reportedUser' => isset($d['reportedUserName']) ? (string) $d['reportedUserName'] : (isset($d['reportedUser']) ? (string) $d['reportedUser'] : ''),
			'reason' => isset($d['reason']) ? (string) $d['reason'] : (isset($d['category']) ? (string) $d['category'] : ''),
			'priority' => $priority !== '' ? $priority : 'medium',
			'status' => $status !== '' ? $status : 'pending',
			'createdAt' => $createdAt,
			'createdAtLabel' => $this->fmt_dt($createdAt),
		);
	}

	private function normalize_subscription($id, $d)
	{
		$status = strtolower(trim((string) (isset($d['status']) ? $d['status'] : '')));
		return array(
			'id' => (string) $id,
			'user' => isset($d['userName']) ? (string) $d['userName'] : '',
			'status' => $status !== '' ? $status : 'expired',
			'amountCents' => isset($d['amountCents']) ? (int) $d['amountCents'] : (isset($d['amount']) ? (int) $d['amount'] : 0),
		);
	}

	private function normalize_transaction($id, $d)
	{
		$createdAt = $this->to_millis(isset($d['createdAt']) ? $d['createdAt'] : (isset($d['date']) ? $d['date'] : 0));
		$status = strtolower(trim((string) (isset($d['status']) ? $d['status'] : (isset($d['paymentStatus']) ? $d['paymentStatus'] : ''))));
		if ($status === 'success' || $status === 'paid' || $status === 'completed') {
			$status = 'successful';
		}
		$cents = isset($d['amountCents']) ? (int) $d['amountCents'] : 0;
		if ($cents <= 0 && isset($d['amount'])) {
			$amt = $d['amount'];
			if (is_numeric($amt)) {
				$cents = ((float) $amt > 50) ? (int) round((float) $amt) : (int) round((float) $amt * 100);
			}
		}
		return array(
			'id' => isset($d['transactionId']) ? (string) $d['transactionId'] : (string) $id,
			'user' => isset($d['userName']) ? (string) $d['userName'] : (isset($d['user']) ? (string) $d['user'] : ''),
			'amountCents' => $cents,
			'amountLabel' => $this->fmt_money($cents),
			'currency' => isset($d['currency']) ? strtoupper((string) $d['currency']) : 'USD',
			'status' => $status !== '' ? $status : 'pending',
			'platform' => isset($d['platform']) ? (string) $d['platform'] : '',
			'createdAt' => $createdAt,
			'createdAtLabel' => $this->fmt_dt($createdAt),
		);
	}

	private function resolve_post_status($d)
	{
		$raw = strtolower(trim((string) (isset($d['status']) ? $d['status'] : '')));
		if (!empty($d['isRemoved']) || $raw === 'removed') {
			return 'removed';
		}
		if (!empty($d['isHidden']) || $raw === 'hidden') {
			return 'hidden';
		}
		if ($raw === '' || $raw === 'visible' || $raw === 'active' || $raw === 'public') {
			return 'visible';
		}
		return $raw;
	}

	private function resolve_user_status($d)
	{
		$raw = strtolower(trim((string) (isset($d['status']) ? $d['status'] : '')));
		if (!empty($d['isDeleted']) || $raw === 'deleted') {
			return 'deleted';
		}
		if (!empty($d['isBlocked']) || $raw === 'blocked' || $raw === 'banned') {
			return 'blocked';
		}
		if (!empty($d['isSuspended']) || $raw === 'suspended') {
			return 'suspended';
		}
		if ($raw === 'active' || $raw === '') {
			return 'active';
		}
		return $raw;
	}

	private function string_list($value)
	{
		$out = array();
		if (!is_array($value)) {
			return $out;
		}
		foreach ($value as $item) {
			if (is_string($item) && trim($item) !== '') {
				$out[] = trim($item);
			}
		}
		return $out;
	}

	private function series_by_day($rows, $field, $days)
	{
		$days = (int) $days;
		$tz = new \DateTimeZone('Asia/Karachi');
		$end = new \DateTime('today', $tz);
		$labels = array();
		$counts = array();
		$map = array();
		for ($i = $days - 1; $i >= 0; $i--) {
			$d = clone $end;
			$d->modify('-' . $i . ' day');
			$key = $d->format('Y-m-d');
			$labels[] = $d->format('d M');
			$map[$key] = 0;
		}
		foreach ($rows as $row) {
			$ms = isset($row[$field]) ? (int) $row[$field] : 0;
			if ($ms <= 0) {
				continue;
			}
			if ($ms < 100000000000) {
				$ms *= 1000;
			}
			$dt = new \DateTime('@' . (int) floor($ms / 1000));
			$dt->setTimezone($tz);
			$key = $dt->format('Y-m-d');
			if (isset($map[$key])) {
				$map[$key]++;
			}
		}
		foreach ($map as $v) {
			$counts[] = $v;
		}
		return array('labels' => $labels, 'data' => $counts);
	}

	private function count_by_key($rows, $key, $statusFilter = '')
	{
		$counts = array();
		foreach ($rows as $row) {
			if ($statusFilter !== '' && (!isset($row['status']) || $row['status'] !== $statusFilter)) {
				continue;
			}
			$label = isset($row[$key]) ? trim((string) $row[$key]) : '';
			if ($label === '') {
				$label = 'Unknown';
			}
			if (!isset($counts[$label])) {
				$counts[$label] = 0;
			}
			$counts[$label]++;
		}
		ksort($counts);
		return array(
			'labels' => array_keys($counts),
			'data' => array_values($counts),
		);
	}

	private function count_topics($posts)
	{
		$counts = array();
		foreach ($posts as $p) {
			$topics = isset($p['topics']) && is_array($p['topics']) ? $p['topics'] : array();
			if (empty($topics) && !empty($p['category'])) {
				$topics = array($p['category']);
			}
			foreach ($topics as $t) {
				$t = trim((string) $t);
				if ($t === '') {
					continue;
				}
				if (!isset($counts[$t])) {
					$counts[$t] = 0;
				}
				$counts[$t]++;
			}
		}
		arsort($counts);
		$counts = array_slice($counts, 0, 8, true);
		return array(
			'labels' => array_keys($counts),
			'data' => array_values($counts),
		);
	}

	private function revenue_by_day($rows, $days)
	{
		$base = $this->series_by_day($rows, 'createdAt', $days);
		$tz = new \DateTimeZone('Asia/Karachi');
		$end = new \DateTime('today', $tz);
		$map = array();
		for ($i = $days - 1; $i >= 0; $i--) {
			$d = clone $end;
			$d->modify('-' . $i . ' day');
			$map[$d->format('Y-m-d')] = 0;
		}
		foreach ($rows as $row) {
			if (!isset($row['status']) || $row['status'] !== 'successful') {
				continue;
			}
			$ms = (int) $row['createdAt'];
			if ($ms <= 0) {
				continue;
			}
			if ($ms < 100000000000) {
				$ms *= 1000;
			}
			$dt = new \DateTime('@' . (int) floor($ms / 1000));
			$dt->setTimezone($tz);
			$key = $dt->format('Y-m-d');
			if (isset($map[$key])) {
				$map[$key] += ((int) $row['amountCents']) / 100;
			}
		}
		$base['data'] = array_values($map);
		return $base;
	}

	public function to_millis($value)
	{
		if ($value === null || $value === '') {
			return 0;
		}
		if (is_int($value) || is_float($value) || is_numeric($value)) {
			return (int) $value;
		}
		if (is_object($value)) {
			if (method_exists($value, 'getTimestamp')) {
				try {
					return (int) ($value->getTimestamp() * 1000);
				} catch (\Throwable $e) {
					// continue
				}
			}
			if (method_exists($value, 'get')) {
				$dt = $value->get();
				if ($dt instanceof \DateTimeInterface) {
					return (int) ($dt->getTimestamp() * 1000);
				}
			}
			if (method_exists($value, 'formatAsString')) {
				$ts = strtotime((string) $value->formatAsString());
				if ($ts) {
					return (int) ($ts * 1000);
				}
			}
			if ($value instanceof \DateTimeInterface) {
				return (int) ($value->getTimestamp() * 1000);
			}
		}
		return 0;
	}

	public function fmt_dt($millis)
	{
		$millis = (int) $millis;
		if ($millis <= 0) {
			return '—';
		}
		if ($millis < 100000000000) {
			$millis *= 1000;
		}
		$dt = new \DateTime('@' . (int) floor($millis / 1000));
		$dt->setTimezone(new \DateTimeZone('Asia/Karachi'));
		return $dt->format('d M Y, h:i A');
	}

	public function fmt_money($cents)
	{
		return '$' . number_format(((int) $cents) / 100, 2);
	}

	public function allowed_user_statuses()
	{
		return array('active', 'suspended', 'blocked', 'deleted');
	}
}
