/**
 * SpaceConnection — bilateral typed relationships between spaces.
 *
 * Distinct from nesting (SpaceRef: unilateral visual embed) and LayerFlow
 * (intra-space module flows). SpaceConnections are stored in BOTH spaces'
 * Automerge docs with the same ID, enabling economic streams, delegative
 * authority, and information sharing across space boundaries.
 */

import type { FlowKind } from "./layer-types";

// ── Connection state machine ──

export type ConnectionState = "proposed" | "active" | "paused" | "revoked";

export const CONNECTION_STATE_COLORS: Record<ConnectionState, string> = {
	proposed: "#f59e0b", // amber
	active:   "#22c55e", // green
	paused:   "#64748b", // slate
	revoked:  "#ef4444", // red
};

// ── Directionality ──

export type ConnectionDirection = "outbound" | "inbound" | "bidirectional";

/** Invert direction from the other space's perspective */
export function invertDirection(dir: ConnectionDirection): ConnectionDirection {
	if (dir === "outbound") return "inbound";
	if (dir === "inbound") return "outbound";
	return "bidirectional";
}

// ── Economic stream spec ──

export interface EconomicFlowSpec {
	tokenId: string;          // mint shape ID, ERC-20 address, or slug:mintId
	tokenName?: string;
	rateLimit: number;        // max tokens per window (0 = unlimited)
	rateWindowMs: number;     // window duration in ms
	requireApproval: boolean; // per-transfer approval gate
	minTransfer?: number;
	maxTransfer?: number;
}

// ── Delegation spec (liquid democracy) ──

export type DelegableAuthority =
	| "voting"
	| "moderation"
	| "curation"
	| "treasury"
	| "membership"
	| "custom";

export interface DelegationSpec {
	authority: DelegableAuthority;
	scope?: string;           // human-readable scope description
	maxDepth: number;         // re-delegation depth (0 = no chain)
	weight: number;           // 0-1 fractional delegation
	retainAuthority: boolean; // delegator keeps authority alongside delegate
	expiry?: number;          // unix timestamp
}

// ── Information stream spec ──

export interface InformationFlowSpec {
	allowedShapeTypes?: string[];  // shape types that can flow (empty = all)
	allowedTags?: string[];
	allowedModules?: string[];
	feedIds?: string[];            // subscribe to specific feeds
	maxItemsPerSync?: number;
	historicalSync: boolean;       // include history or only new items
}

// ── Per-connection permissions ──

export interface ConnectionPermissions {
	read: boolean;
	write: boolean;
	configure: boolean;      // can modify connection params
	pause: boolean;
	revoke: boolean;
	expiry?: number;         // auto-expire unix timestamp
}

// ── Per-space connection policy ──

export interface ConnectionPolicy {
	inboundConsent: "open" | "members" | "approval" | "closed";
	outboundConsent: "open" | "members" | "approval" | "closed";
	defaultPermissions: ConnectionPermissions;
	acceptedFlowKinds: FlowKind[];
	allowlist?: string[];    // slugs bypassing consent
	blocklist?: string[];    // slugs always denied
}

// ── The SpaceConnection record (stored in BOTH spaces' docs) ──

export interface SpaceConnection {
	id: string;                    // shared ID (same in both docs)
	localSlug: string;             // this space
	remoteSlug: string;            // other space
	remoteDID?: string;            // remote owner DID at creation time
	direction: ConnectionDirection; // from this space's perspective
	flowKinds: FlowKind[];
	state: ConnectionState;
	economicFlows?: EconomicFlowSpec[];
	delegations?: DelegationSpec[];
	informationFlow?: InformationFlowSpec;
	permissions: ConnectionPermissions;
	strength: number;              // 0-1, visual membrane permeability
	label?: string;
	meta?: Record<string, unknown>;
	proposedBy: string;            // DID
	proposedAt: number;
	acceptedBy?: string;
	acceptedAt?: number;
	lastModifiedBy?: string;
	lastModifiedAt?: number;
	stateReason?: string;          // reason for pause/revoke
}

// ── Pending connection request (in-memory, like PendingNestRequest) ──

export type ConnectionRequestStatus = "pending" | "accepted" | "rejected";

export interface PendingConnectionRequest {
	id: string;
	fromSlug: string;
	toSlug: string;
	proposedBy: string;            // DID
	direction: ConnectionDirection;
	flowKinds: FlowKind[];
	permissions: ConnectionPermissions;
	strength: number;
	label?: string;
	economicFlows?: EconomicFlowSpec[];
	delegations?: DelegationSpec[];
	informationFlow?: InformationFlowSpec;
	meta?: Record<string, unknown>;
	message?: string;
	status: ConnectionRequestStatus;
	createdAt: number;
	resolvedAt?: number;
	resolvedBy?: string;
}

// ── Derived membrane permeability (for visualization) ──

export interface MembranePermeability {
	inbound: { kind: FlowKind; strength: number; connectionId: string }[];
	outbound: { kind: FlowKind; strength: number; connectionId: string }[];
	overallPermeability: number;   // 0 = sealed, 1 = fully open
}

// ── Default policies ──

export const DEFAULT_PERSONAL_CONNECTION_POLICY: ConnectionPolicy = {
	inboundConsent: "approval",
	outboundConsent: "approval",
	defaultPermissions: {
		read: true,
		write: false,
		configure: false,
		pause: true,
		revoke: true,
	},
	acceptedFlowKinds: ["data", "trust"],
};

export const DEFAULT_COMMUNITY_CONNECTION_POLICY: ConnectionPolicy = {
	inboundConsent: "members",
	outboundConsent: "members",
	defaultPermissions: {
		read: true,
		write: true,
		configure: false,
		pause: true,
		revoke: true,
	},
	acceptedFlowKinds: ["economic", "trust", "data", "governance", "resource"],
};

// ── Helpers ──

/**
 * Cascade connection permissions across a nesting chain (intersection).
 * When space A nests B which has connections, the effective permissions
 * are the intersection of the nest permissions and connection permissions.
 */
export function cascadeConnectionPermissions(
	chain: ConnectionPermissions[],
): ConnectionPermissions {
	return {
		read: chain.every(p => p.read),
		write: chain.every(p => p.write),
		configure: chain.every(p => p.configure),
		pause: chain.every(p => p.pause),
		revoke: chain.every(p => p.revoke),
		expiry: chain.some(p => p.expiry)
			? Math.min(...chain.filter(p => p.expiry).map(p => p.expiry!))
			: undefined,
	};
}

/**
 * Compute membrane permeability from a set of active connections.
 */
export function computeMembranePermeability(
	connections: SpaceConnection[],
	localSlug: string,
): MembranePermeability {
	const inbound: MembranePermeability["inbound"] = [];
	const outbound: MembranePermeability["outbound"] = [];

	for (const conn of connections) {
		if (conn.state !== "active") continue;

		for (const kind of conn.flowKinds) {
			const entry = { kind, strength: conn.strength, connectionId: conn.id };

			if (conn.direction === "inbound" || conn.direction === "bidirectional") {
				inbound.push(entry);
			}
			if (conn.direction === "outbound" || conn.direction === "bidirectional") {
				outbound.push(entry);
			}
		}
	}

	// Overall permeability: average of all active connection strengths, or 0 if none
	const activeConns = connections.filter(c => c.state === "active");
	const overallPermeability = activeConns.length > 0
		? activeConns.reduce((sum, c) => sum + c.strength, 0) / activeConns.length
		: 0;

	return { inbound, outbound, overallPermeability };
}
