<%- include('top') %>

<script type="text/javascript">
	app.auth.forceLogin();
	var currentUser;
	var isOwnProfile = !location.pathname.includes('/users/');

	function renderProfile(user){
		// data.photo = unescape(encodeURIComponent(data.jpegPhoto));
		user.createTimestamp = moment(user.createTimestamp, "YYYYMMDDHHmmssZ").fromNow();
		user.modifyTimestamp = moment(user.modifyTimestamp, "YYYYMMDDHHmmssZ").fromNow();
		user.managerUids = (user.manager || []).map(app.user.dnToUid);

		$.scope.user.update(user);
		$.scope.passwordReset.update(user);
	};

	async function renderUserGroups(user){
		try{
			let res = await app.api.get('group/?detail=true&member='+user.uid);
			$.scope.mygroups.empty();
			$.scope.mygroups.push(...res.results);
		}catch(error){
			console.error('renderUserGroups error:', error)
		}
	}

	async function removeFromGroup(cn, btn){
		const $row = $(btn).closest('tr');
		const confirmed = await app.messages.confirm(`Remove ${currentUser.uid} from "${cn}"?`, $row, 'warning');
		if (!confirmed) return;
		app.api.delete('group/' + encodeURIComponent(cn) + '/' + encodeURIComponent(currentUser.uid), function(error, data){
			if(error){ app.messages.action((data && data.message) || 'Failed to remove from group', $row, 'danger'); return; }
			$.scope.mygroups.remove('cn', cn);
		});
	}

	var addGroupSelect;
	async function addToGroups(btn){
		const cns = addGroupSelect.get();
		if(!cns.length) return;
		const $card = $(btn).closest('.card-body');
		for(const cn of cns){
			await new Promise(function(resolve){
				app.api.put('group/' + encodeURIComponent(cn) + '/' + encodeURIComponent(currentUser.uid), {}, function(error, data){
					if(error) app.messages.action((data && data.message) || `Failed to add to "${cn}"`, $card, 'danger');
					resolve();
				});
			});
		}
		addGroupSelect.clear();
		renderUserGroups(currentUser);
	}

	async function renderPersonalGroupMembers(user){
		try{
			let res = await app.api.get('user/' + user.uid + '/group-members');
			$.scope.personalGroupMembers.empty();
			$.scope.personalGroupMembers.push(...(res.results || []).map(uid => ({uid})));
		}catch(error){
			console.error('renderPersonalGroupMembers error:', error)
		}
	}

	async function removePersonalGroupMember(memberUid, btn){
		const $row = $(btn).closest('tr');
		const confirmed = await app.messages.confirm(`Remove ${memberUid} from ${currentUser.uid}'s group?`, $row, 'warning');
		if (!confirmed) return;
		app.api.delete('user/' + encodeURIComponent(currentUser.uid) + '/group-member/' + encodeURIComponent(memberUid), function(error, data){
			if(error){ app.messages.action((data && data.message) || 'Failed to remove from group', $row, 'danger'); return; }
			$.scope.personalGroupMembers.remove('uid', memberUid);
		});
	}

	var addPersonalGroupMemberSelect;
	async function addPersonalGroupMembers(btn){
		const uids = addPersonalGroupMemberSelect.get();
		if(!uids.length) return;
		const $card = $(btn).closest('.card-body');
		for(const uid of uids){
			await new Promise(function(resolve){
				app.api.put('user/' + encodeURIComponent(currentUser.uid) + '/group-member/' + encodeURIComponent(uid), {}, function(error, data){
					if(error) app.messages.action((data && data.message) || `Failed to add "${uid}"`, $card, 'danger');
					resolve();
				});
			});
		}
		addPersonalGroupMemberSelect.clear();
		renderPersonalGroupMembers(currentUser);
	}

	async function renderMyServices(){
		try{
			let res = await app.api.get('discovery/me');
			res.results.forEach(r => {
				r.resolvedAddress = (r.metadata && r.metadata.address) || (r.metadata && r.metadata.ip) || 'N/A';
			});
			$.scope.myservices.empty();
			$.scope.myservices.push(...res.results);
		}catch(error){
			console.error('renderMyServices error:', error)
		}
	}

	async function renderMyMetrics(){
		try{
			let uid = currentUser.uid;
			let res = await app.api.get(`metrics/user/${uid}`);
			if (res && res.results) {
				const renderList = (items, id) => {
					const el = document.getElementById(id);
					if(!el) return;
					el.innerHTML = '';
					if (!items || items.length === 0) {
						el.innerHTML = '<li class="list-group-item text-muted">No data available</li>';
						return;
					}
					items.forEach(item => {
						el.innerHTML += `<li class="list-group-item d-flex justify-content-between align-items-center">
							${item.value}
							<span class="badge bg-primary rounded-pill">${item.score}</span>
						</li>`;
					});
				};
				renderList(res.results.services, 'metrics-user-services');
			}
		}catch(error){
			console.error('renderMyMetrics error:', error)
		}
	}


	async function determinUser(){
		if(location.pathname.includes('/users/')){
			let uid = location.pathname.replace('/users/', '');

			return (await app.api.get('user/'+uid)).results;
		}else{
			return await app.auth.asyncUser;
		}
	}

	function editUser(user){
		user = user || currentUser;

		var $profileCard = $('#userProfile');
		var $editCard = $('#editProfile');

		$.scope.editProfile.update(user);
		// jq-repeat's update() is trailing-edge throttled (~50ms) as of 2.1.0 --
		// wait for the throttle tick to land before sliding the updated card
		// into view, or it can briefly show stale/empty data. The manager
		// picker is a JS widget, not a mustache-bound input, so it also has to
		// wait for update() to (re-)render its empty mount div before attaching.
		setTimeout(function(){
			app.ui.userSelect('#edit-manager', {
				name: 'manager',
				values: user.managerUids || [],
				placeholder: 'Type a username…',
			});
			$profileCard.slideUp();
			$editCard.slideDown();
		}, 60);
	}

	function editUserSeccess(data){
		currentUser = data.results;
		renderProfile(currentUser);
		// Same throttle-tick wait as editUser() above -- renderProfile() calls
		// $.scope.user.update()/passwordReset.update() internally.
		setTimeout(function(){
			$('#editProfile').slideUp();
			$('#userProfile').slideDown()
		}, 60);
	}

	async function toggleActive(uid, active){
		app.user.setActive(uid, active, async function(error, data){
			if(error) return app.messages.toast('Failed to update user status', 'danger');
			currentUser = await determinUser();
			renderProfile(currentUser);
		});
	}

	async function deleteUser(uid, btn){
		const $card = $(btn).closest('.card');
		const confirmed = await app.messages.confirm(`Delete user "${uid}"?`, $card, 'warning');
		if (!confirmed) return;
		app.api.delete('user/' + uid, function(error, data){
			if (error) {
				app.messages.action(data.message || 'Failed to delete user', $card, 'danger');
				return;
			}
			window.location.href = '/users';
		});
	}

	$(document).ready(async function(){
		currentUser = await determinUser();

		renderProfile(currentUser);
		renderUserGroups(currentUser);
		renderPersonalGroupMembers(currentUser);
		renderMyServices();
		if(!isOwnProfile) {
			renderMyMetrics();
			$('#user-metrics-card').show();
		}
		$('#personal-group-uid-label').text(currentUser.uid);

		addGroupSelect = app.ui.groupSelect('#add-group-select', {
			name: 'groups', values: [], placeholder: 'Type a group name…',
		});
		addPersonalGroupMemberSelect = app.ui.userSelect('#add-personal-group-member-select', {
			name: 'members', values: [], placeholder: 'Type a username…',
		});

		// API Tokens are self-service only — never shown when an admin is
		// viewing someone else's profile via /users/:uid.
		if(isOwnProfile){
			$('#own-api-tokens-section').show();
			tableAJAX();
			$('form[action="api-token/"]').attr('evalAJAX',
				'showSecret(data.token); tableAJAX(); $form.trigger("reset");'
			);
		}
	});
</script>

<div class="row mb-3" style="display:none">
	<div class="col-md-4">
		<div class="shadow-lg card">
			<div class="card-header shadow">
				<i class="fa-solid fa-arrow-rotate-left"></i>
				Password Reset
				<div class="float-end">
					<i class="fa-solid fa-arrows-up-down"></i>
				</div>
			</div>
			<div class="card-header shadow actionMessage" style="display:none">
			</div>
			<div jq-repeat="passwordReset" class="card-body">
				<h3>
					Reset Password for {{uid}}
				</h3>
				<form action="user/{{uid}}/password" method="put" onsubmit="formAJAX(this)" class="mb-3">
					<div class="mb-3">
						<label class="form-label">Password</label>
						<div class="input-group shadow">
							<span class="input-group-text" id="addon-wrapping"><i class="fa-solid fa-key"></i></span>
							<input type="password" name="userPassword" class="form-control" placeholder="huunteR!23" aria-label="Username" aria-describedby="addon-wrapping">
						</div>
					</div>
					<div class="mb-3">
						<label class="form-label">Again</label>
						<div class="input-group shadow">
							<span class="input-group-text" id="addon-wrapping"><i class="fa-solid fa-key"></i></span>
							<input type="password" name="password" class="form-control" placeholder="huunteR!23" aria-label="Username" aria-describedby="addon-wrapping">
						</div>
					</div>
					<button type="submit" class="btn btn-outline-secondary shadow">Change</button>
				</form>

			</div>
		</div>
	</div>

	<div class="col-md-8">
		<div id="userProfile" class="shadow-lg card card-default mb-8">
			<div class="card-header shadow">
				<i class="fa-regular fa-id-card"></i>
				Profile
				<div class="float-end">
					<i class="fa-solid fa-arrows-up-down"></i>
				</div>
			</div>
			<div class="card-header shadow actionMessage" style="display:none">
			</div>
			<div class="profile-body" jq-repeat="user">
				<div class="card-body profile-body-{{uid}}">
					<h2><i>User Name:</i> <b>{{uid}}</b></h2>
					{{^isServiceAccount}}<i>Name:</i> <b>{{givenName}} {{sn}}</b><br />{{/isServiceAccount}}
					<i>Email:</i> <b>{{mail}}</b>
					{{#emailVerified}}<span class="badge bg-success ms-1"><i class="fa-solid fa-circle-check"></i> Verified</span>{{/emailVerified}}
					<br />
					<i>Phone:</i> <b>{{mobile}}</b>
					{{#phoneVerified}}<span class="badge bg-success ms-1"><i class="fa-solid fa-circle-check"></i> Verified</span>{{/phoneVerified}}
					<br />
					<i>Location (Site):</i> <b>{{location}} </b><br />
					<i>LDAP DN:</i> <b>{{dn}} </b><br />
					<i>Home Directory:</i> <b>{{homeDirectory}} </b><br />
					<i>Login Shell:</i> <b>{{loginShell}} </b><br />
					<i>Manager(s):</i>
					{{#managerUids}}<span class="badge bg-secondary me-1">{{.}}</span>{{/managerUids}}
					<br />
					<i>Status:</i>
					{{#isActive}}<span class="badge bg-success">Active</span>{{/isActive}}
					{{#isInactive}}<span class="badge bg-danger">Inactive</span>{{/isInactive}}
					<br />
					<i>Date of Birth:</i> <b>{{dateOfBirth}}</b><br />
					<i>TOS:</i>
					{{#tosAccepted}}<span class="badge bg-success">Accepted</span>{{/tosAccepted}}
					{{#tosNotAccepted}}<span class="badge bg-warning text-dark">Pending</span>{{/tosNotAccepted}}
					<br />
					<i>SSH Public Key:</i> <b>{{sshPublicKey}}</b><br />
					<i>Unix User ID:</i> <b>{{uidNumber}} </b><br />
					<i>Unix Group ID:</i> <b>{{gidNumber}} </b><br />
					<i>Description:</i><br>
					<p>
						{{description}}
					</p>
					<!-- <img id="profile_photo" /> -->
				</div>
				<div class="card-footer profile-body-{{uid}}">
					<div class="float-end">
						<button type="button" onclick="editUser()" class="btn btn-warning btn shadow ">
							<i class="fa-solid fa-user-pen"></i>
						</button>
						{{#isActive}}
						<button type="button" class="btn btn-outline-warning shadow group-required group-required-app_sso_admin" title="Deactivate user" onclick="toggleActive('{{uid}}', false)">
							<i class="fa-solid fa-lock"></i>
						</button>
						{{/isActive}}
						{{#isInactive}}
						<button type="button" class="btn btn-warning shadow group-required group-required-app_sso_admin" title="Activate user" onclick="toggleActive('{{uid}}', true)">
							<i class="fa-solid fa-lock-open"></i>
						</button>
						{{/isInactive}}
						<button type="button" class="btn btn-secondary shadow group-required group-required-app_sso_admin" title="Impersonate this user" onclick="startImpersonate('{{uid}}')">
							<i class="fa-solid fa-user-secret"></i>
						</button>
						<button type="button" class="btn btn-danger shadow group-required group-required-app_sso_admin" onclick="deleteUser('{{uid}}', this)">
							<i class="fa-solid fa-user-slash"></i>
						</button>
					</div>
					<div class="float-left">
						<i>Joined:</i> <b>{{createTimestamp}} </b> <br/>
						<i>Edited:</i> <b>{{modifyTimestamp}} </b>
					</div>

				</div>
			</div>
		</div>

		<div id="editProfile" class="shadow-lg card card-default mb-8" style="display:none">
			<div class="card-header shadow">
				<i class="fad fa-id-card"></i>
				Edit Profile
				<div class="float-end">
					<i class="fa-solid fa-arrows-up-down"></i>
				</div>
			</div>
			<div class="card-header shadow actionMessage" style="display:none">
			</div>
			<div class="card-body" jq-repeat="editProfile">
				<div id="tableAJAX">
					<h3>Editing {{uid}}</h3>
					<form action="user/{{uid}}" method="put" onsubmit="formAJAX(this)" evalAJAX="editUserSeccess(data)">
						<div class="mb-3">
							<label class="form-label">Date of Birth</label>
							<input type="date" class="form-control shadow" name="dateOfBirth" value="{{dateOfBirth}}" />
						</div>

						<div class="mb-3">
							<label class="form-label">SSH Public Key</label>
							<input type="text" class="form-control" name="sshPublicKey" placeholder="ssh-rsa AAAAB3NzaC1yc2EAAAADAQ..." value="{{sshPublicKey}}" />
						</div>

						<div class="mb-3">
							<label class="form-label">Mobile Phone</label>
							<input type="text" class="form-control" name="mobile" placeholder="9175551234" value="{{mobile}}" />
						</div>
						<div class="mb-3">
							<label class="form-label">Location (Site)</label>
							<input type="text" class="form-control" name="location" placeholder="Site One" value="{{location}}" />
						</div>
						<div class="mb-3">
							<label class="form-label">Home Directory</label>
							<input type="text" class="form-control" name="homeDirectory" placeholder="/home/jsmith" value="{{homeDirectory}}" />
						</div>
						<div class="mb-3">
							<label class="form-label">Login Shell</label>
							<input type="text" class="form-control" name="loginShell" placeholder="/bin/bash" value="{{loginShell}}" />
						</div>
						<div class="mb-3">
							<label class="form-label">Manager(s)</label>
							<div id="edit-manager"></div>
						</div>
						<div class="mb-3">
							<label class="form-label">User Description (Optional)</label>
							<textarea class="form-control" name="description" placeholder="Admin group for gitea app">{{description}}</textarea>
						</div>
						<button type="submit" class="btn btn-outline-dark btn-warning">Change</button>

					</form>
				</div>
			</div>
		</div>

		<div class="shadow-lg card card-default mb-8">
			<div class="card-header shadow">
				<i class="fa-solid fa-users-viewfinder"></i>
				My groups
				<div class="float-end">
					<a href="/docs/accounts" class="text-reset me-2" title="Help"><i class="fa-solid fa-circle-question"></i></a>
					<i class="fa-solid fa-arrows-up-down"></i>
				</div>
			</div>
			<div class="card-header shadow actionMessage" style="display:none">
			</div>
			<div class="card-body">
				<div class="table-responsive">
					<table class="table">
						<thead>
							<th>
								Name
							</th>
							<th>
								Description
							</th>
							<th class="group-required group-required-app_sso_admin"></th>
						</thead>
						<tbody jq-repeat="mygroups">
							<tr>
								<td>{{cn}}</td>
								<td>{{description}}</td>
								<td class="text-end group-required group-required-app_sso_admin">
									<button type="button" class="btn btn-sm btn-outline-danger" title="Remove from group" onclick="removeFromGroup('{{cn}}', this)">
										<i class="fa-solid fa-xmark"></i>
									</button>
								</td>
							</tr>
						</tbody>
					</table>
				</div>
				<div class="group-required group-required-app_sso_admin">
					<label class="form-label small">Add to group</label>
					<div class="d-flex gap-2 align-items-start">
						<div id="add-group-select" class="flex-grow-1"></div>
						<button type="button" class="btn btn-outline-dark" onclick="addToGroups(this)">Add</button>
					</div>
				</div>
			</div>
		</div>

		<div class="shadow-lg card card-default mb-8">
			<div class="card-header shadow">
				<i class="fa-solid fa-layer-group"></i>
				My Services
				<div class="float-end">
					<i class="fa-solid fa-arrows-up-down"></i>
				</div>
			</div>
			<div class="card-body">
				<div class="table-responsive">
					<table class="table">
						<thead>
							<th>Name</th>
							<th>Address / IP</th>
							<th>Description</th>
							<th>Kind</th>
						</thead>
						<tbody jq-repeat="myservices">
							<tr>
								<td>{{name}} <small class="text-muted">({{slug}})</small></td>
								<td><a href="{{resolvedAddress}}" target="_blank"><code>{{resolvedAddress}}</code></a></td>
								<td>{{description}}</td>
								<td>
									{{#metadata.isProduction}}<span class="badge bg-danger mb-1">Prod</span><br>{{/metadata.isProduction}}
									<span class="badge bg-secondary">{{kind}}{{#metadata.subType}} ({{metadata.subType}}){{/metadata.subType}}</span>
								</td>
							</tr>
						</tbody>
					</table>
				</div>
				</div>
			</div>
		</div>

		<div class="shadow-lg card card-default mb-8 group-required group-required-app_sso_admin" id="user-metrics-card" style="display:none">
			<div class="card-header shadow bg-success text-white">
				<i class="fa-solid fa-chart-line"></i>
				Security & Usage Stats (Last 7 Days)
				<div class="float-end">
					<i class="fa-solid fa-arrows-up-down"></i>
				</div>
			</div>
			<div class="card-body p-0">
				<div class="row m-0">
					<div class="col-12 p-3">
						<h6 class="text-success"><i class="fa-solid fa-plug"></i> Top Services Used</h6>
						<ul class="list-group list-group-flush border rounded" id="metrics-user-services">
							<li class="list-group-item text-muted">Loading...</li>
						</ul>
					</div>
				</div>
			</div>
		</div>

		<div class="shadow-lg card card-default mb-8 group-required group-required-app_sso_admin">
			<div class="card-header shadow">
				<i class="fa-solid fa-people-group"></i>
				Members of <span id="personal-group-uid-label"></span>'s group
				<div class="float-end">
					<a href="/docs/accounts" class="text-reset me-2" title="Help"><i class="fa-solid fa-circle-question"></i></a>
					<i class="fa-solid fa-arrows-up-down"></i>
				</div>
			</div>
			<div class="card-header shadow actionMessage" style="display:none">
			</div>
			<div class="card-body">
				<p class="text-muted small">
					Every account gets a personal Unix group (its primary GID) — add
					other accounts here as supplementary members (e.g. to share write
					access to files owned by this group).
				</p>
				<div class="table-responsive">
					<table class="table">
						<thead>
							<th>
								Username
							</th>
							<th class="text-end"></th>
						</thead>
						<tbody jq-repeat="personalGroupMembers">
							<tr>
								<td>{{uid}}</td>
								<td class="text-end">
									<button type="button" class="btn btn-sm btn-outline-danger" title="Remove from group" onclick="removePersonalGroupMember('{{uid}}', this)">
										<i class="fa-solid fa-xmark"></i>
									</button>
								</td>
							</tr>
						</tbody>
					</table>
				</div>
				<label class="form-label small">Add member</label>
				<div class="d-flex gap-2 align-items-start">
					<div id="add-personal-group-member-select" class="flex-grow-1"></div>
					<button type="button" class="btn btn-outline-dark" onclick="addPersonalGroupMembers(this)">Add</button>
				</div>
			</div>
		</div>
	</div>
</div>
</div>

<!-- Token modal (shown once on create/rotate) -->
<div class="modal fade" id="secretModal" tabindex="-1">
	<div class="modal-dialog">
		<div class="modal-content">
			<div class="modal-header">
				<h5 class="modal-title"><i class="fa-solid fa-key"></i> API Token</h5>
				<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
			</div>
			<div class="modal-body">
				<p class="text-danger"><i class="fa-solid fa-triangle-exclamation"></i> Save this token now — it will <strong>not</strong> be shown again.</p>
				<div class="input-group">
					<input type="text" id="secretValue" class="form-control font-monospace" readonly>
					<button class="btn btn-outline-secondary" onclick="copySecret()" title="Copy">
						<i class="fa-solid fa-copy"></i>
					</button>
				</div>
				<p class="mt-3 mb-0 text-muted small">Use it as a bearer token:<br><code>Authorization: Bearer &lt;token&gt;</code></p>
			</div>
			<div class="modal-footer">
				<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Done</button>
			</div>
		</div>
	</div>
</div>

<!-- Edit modal -->
<div class="modal fade" id="editModal" tabindex="-1">
	<div class="modal-dialog">
		<div class="modal-content">
			<div class="modal-header">
				<h5 class="modal-title"><i class="fa-solid fa-pen-to-square"></i> Edit API Token</h5>
				<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
			</div>
			<div class="modal-body">
				<div class="card-header actionMessage mb-3" style="display:none"></div>
				<input type="hidden" id="edit-id">
				<div class="mb-3">
					<label class="form-label">Name</label>
					<input type="text" id="edit-name" class="form-control shadow">
				</div>
				<div class="mb-3">
					<label class="form-label">Description</label>
					<input type="text" id="edit-description" class="form-control shadow">
				</div>
				<div class="mb-3">
					<label class="form-label">Expires in (days) <small class="text-muted">(0 = never)</small></label>
					<input type="number" id="edit-expires_in_days" class="form-control shadow" min="0">
				</div>
			</div>
			<div class="modal-footer">
				<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
				<button type="button" class="btn btn-primary" onclick="saveEdit(this)"><i class="fa-solid fa-floppy-disk"></i> Save</button>
			</div>
		</div>
	</div>
</div>

<script type="text/javascript">
	// Any logged-in user can manage their own API tokens (self-service).
	// Section is only revealed (see $(document).ready above) when isOwnProfile.

	var secretModal = new bootstrap.Modal(document.getElementById('secretModal'));
	var editModal   = new bootstrap.Modal(document.getElementById('editModal'));
	var tokensById = {};

	function showSecret(secret){
		document.getElementById('secretValue').value = secret;
		secretModal.show();
	}
	function copySecret(){ copyField('secretValue'); }

	function copyField(id, btn){
		var el = document.getElementById(id);
		if(!el) return;
		el.select(); el.setSelectionRange(0, 99999); document.execCommand('copy');
		if(btn){ var $i = $(btn).find('i'), prev = $i.attr('class');
			$i.attr('class', 'fa-solid fa-check'); setTimeout(function(){ $i.attr('class', prev); }, 1200); }
	}

	function fmtTime(ms){
		// created_on/last_used_on come back from Redis as strings (model-redis
		// only coerces fields with an explicit `type`); moment(value, "x") parses
		// a numeric string-or-number as a Unix-ms timestamp, unlike new Date(str).
		if(!ms || Number(ms) === 0) return '—';
		var t = moment(ms, "x");
		if(!t.isValid()) return '—';
		return t.fromNow() + ' <span class="text-muted">(' + t.format('YYYY-MM-DD HH:mm') + ')</span>';
	}
	function fmtExpiry(token){
		// expires_at is type:number (a real number); isExpired is a class getter
		// that is NOT serialized to the client, so compute expiry here.
		var exp = Number(token.expires_at);
		if(!exp) return '<span class="badge bg-secondary">never</span>';
		if(Date.now() > exp) return '<span class="badge bg-danger">expired</span>';
		return '<span class="badge bg-warning text-dark">' + moment(exp, "x").fromNow() + '</span>';
	}

	function processToken(token){
		tokensById[token.id] = token;
		token.id_short = token.id.slice(0, 12) + '…';
		token.expires_display = fmtExpiry(token);
		token.created_display  = fmtTime(token.created_on);
		token.last_used_display = fmtTime(token.last_used_on);
		return token;
	}

	async function tableAJAX(){
		let data = await app.apiToken.list();
		$.scope.apiTokenCard.empty();
		$.each(data.results, function(_, token){
			$.scope.apiTokenCard.push(processToken(token));
		});
	}

	async function revokeToken(id, name, btn){
		var $card = $(btn).closest('.card');
		$card.addClass('table-warning');
		var confirmed = await app.messages.confirm('Revoke API token "' + name + '"? It stops working immediately.', $card, 'warning');
		$card.removeClass('table-warning');
		if(!confirmed) return;
		app.apiToken.remove({id: id}, function(error, data){
			if(error){ app.messages.action('Error: ' + data.message, $card, 'danger'); return; }
			$.scope.apiTokenCard.remove('id', id);
		});
	}

	async function rotateToken(id, name, btn){
		var $card = $(btn).closest('.card');
		var confirmed = await app.messages.confirm('Rotate API token "' + name + '"? The old token stops working immediately.', $card, 'warning');
		if(!confirmed) return;
		app.apiToken.rotate({id: id}, function(error, data){
			if(error){ app.messages.action('Error: ' + data.message, $card, 'danger'); return; }
			showSecret(data.token);
			tableAJAX();
		});
	}

	function editToken(id){
		var t = tokensById[id]; if(!t) return;
		$('#edit-id').val(id);
		$('#edit-name').val(t.name || '');
		$('#edit-description').val(t.description || '');
		$('#edit-expires_in_days').val('');
		editModal.show();
	}

	function saveEdit(btn){
		var $msg = $('#editModal .actionMessage');
		var payload = {
			id: $('#edit-id').val(),
			name: $('#edit-name').val(),
			description: $('#edit-description').val(),
			expires_in_days: $('#edit-expires_in_days').val(),
		};
		app.apiToken.update(payload, function(error, data){
			if(error){ app.messages.action((data && data.message) || 'Update failed.', $msg.parent(), 'danger'); return; }
			editModal.hide();
			tableAJAX();
		});
	}
</script>

<!-- Not class="row": app-base.js's `$('div.row').fadeIn('slow')` page-reveal
     runs before this page's own ready handler and would unhide any div.row
     unconditionally, defeating the isOwnProfile check below. -->
<div id="own-api-tokens-section" style="display:none">
<div class="row mt-3">
	<div class="col-12">
		<h5 class="mb-3"><i class="fa-solid fa-code"></i> API Tokens</h5>
	</div>
	<div class="col-md-4">
		<div class="card shadow-lg">
			<div class="card-header"><i class="fa-solid fa-plus"></i> New API Token
				<a href="/docs/api-tokens" class="text-reset float-end" title="Help"><i class="fa-solid fa-circle-question"></i></a>
			</div>
			<div class="card-header actionMessage" style="display:none"></div>
			<div class="card-body">
				<p class="text-muted small">A personal access token lets scripts and services call the SSO management API as you, with your permissions. Treat it like a password.</p>
				<form action="api-token/" method="post" onsubmit="formAJAX(this)">
					<div class="mb-3">
						<label class="form-label">Name</label>
						<input type="text" class="form-control shadow" name="name" placeholder="CI user sync" validate=":1">
					</div>
					<div class="mb-3">
						<label class="form-label">Description</label>
						<input type="text" class="form-control shadow" name="description" placeholder="Used by the nightly sync job">
					</div>
					<div class="mb-3">
						<label class="form-label">Expires in (days) <small class="text-muted">(0 = never)</small></label>
						<input type="number" class="form-control shadow" name="expires_in_days" value="0" min="0">
					</div>
					<button type="submit" class="btn btn-outline-dark"><i class="fa-solid fa-plus"></i> Create</button>
				</form>
			</div>
		</div>
	</div>
	<div class="col-md-8">
		<div class="card-header actionMessage" style="display:none"></div>

		<div jq-repeat="apiTokenCard" jq-index-key="id" id="apitoken-card-{{id}}" class="card shadow mb-3">
			<div class="card-header">
				<h5><i class="fa-solid fa-key"></i> {{ name }}</h5>
				<small class="text-muted font-monospace">{{ id_short }}</small>
			</div>
			<div class="card-header actionMessage" style="display:none"></div>
			<div class="card-body">
				{{ #description }}<p>{{ description }}</p>{{ /description }}
				<dl class="row mb-0">
					<dt class="col-sm-3">Token ID</dt>
					<dd class="col-sm-9"><code>{{ id_short }}</code></dd>
					<dt class="col-sm-3">Created</dt>
					<dd class="col-sm-9">{{{ created_display }}}</dd>
					<dt class="col-sm-3">Last used</dt>
					<dd class="col-sm-9">{{{ last_used_display }}}</dd>
					<dt class="col-sm-3">Expires</dt>
					<dd class="col-sm-9">{{{ expires_display }}}</dd>
				</dl>
			</div>
			<div class="card-footer">
				<button type="button" onclick="editToken('{{id}}')" class="btn btn-primary btn-sm"><i class="fa-solid fa-pen-to-square"></i> Edit</button>
				<button type="button" onclick="rotateToken('{{id}}', '{{name}}', this)" class="btn btn-warning btn-sm"><i class="fa-solid fa-arrows-rotate"></i> Rotate</button>
				<button type="button" onclick="revokeToken('{{id}}', '{{name}}', this)" class="btn btn-danger btn-sm float-end"><i class="fa-solid fa-trash"></i> Revoke</button>
			</div>
		</div>
	</div>
</div>
</div>

<%- include('impersonate_modal') %>
<%- include('bottom') %>
