# @timeback/edubridge

TypeScript client for the Edubridge API — simplified course enrollment and analytics, abstracting OneRoster complexity.

## Installation

```bash
bun add @timeback/edubridge
```

## Quick Start

```typescript
import { EdubridgeClient } from '@timeback/edubridge'

const edubridge = new EdubridgeClient({
	env: 'staging', // or 'production'
	auth: {
		clientId: 'your-client-id',
		clientSecret: 'your-client-secret',
	},
})

const enrollment = await edubridge.enrollments.enroll(userId, courseId)
const enrollments = await edubridge.enrollments.getByUser(userId)
const students = await edubridge.users.listStudents()
```

## API Design

### Standalone vs Composed

```typescript
// Composed
import { TimebackClient } from '@timeback/core'

// Standalone
const edubridge = new EdubridgeClient({ env: 'staging', auth })

const client = new TimebackClient({ env: 'staging', auth })
client.edubridge.enrollments.enroll(userId, courseId)
```

### Client Structure

```typescript
const client = new EdubridgeClient(options)

// Enrollments
client.enrollments.getByUser(userId)
client.enrollments.enroll(userId, courseId, schoolId?, options?)
client.enrollments.unenroll(userId, courseId, schoolId?)
client.enrollments.resetGoals(courseId)
client.enrollments.resetProgress(userId, courseId)
client.enrollments.getDefaultClass(courseId, schoolId?)

// Users
client.users.listByRole(params)
client.users.listStudents(params?)
client.users.listTeachers(params?)
client.users.search(roles, searchTerm, limit?)

// Analytics
client.analytics.getActivity(params)
client.analytics.getWeeklyFacts(params)
client.analytics.getEnrollmentFacts(params)
client.analytics.getHighestGradeMastered(studentId, subject)

// Applications
client.applications.list()
client.applications.getMetrics(applicationSourcedId)

// Subject Tracks
client.subjectTracks.list()
client.subjectTracks.get(id)
client.subjectTracks.create(data)
client.subjectTracks.update(id, data)
client.subjectTracks.delete(id)
client.subjectTracks.listGroups()

// Learning Reports
client.learningReports.getMapProfile(userId)
client.learningReports.getTimeSaved(userId)
```

### Enrollments

The enrollments resource handles OneRoster complexity automatically:

```typescript
// Enroll a student in a course (creates class, term, etc. automatically)
const enrollment = await client.enrollments.enroll(userId, courseId, schoolId, {
	role: 'student',
	metadata: {
		goals: {
			dailyXp: 100,
			dailyActiveMinutes: 30,
		},
	},
})

// Get all enrollments for a user
const enrollments = await client.enrollments.getByUser(userId)

// Unenroll from a course
await client.enrollments.unenroll(userId, courseId)

// Reset goals for all users in a course
const result = await client.enrollments.resetGoals(courseId)
console.log(`Updated ${result.updated} enrollments`)
```

### Analytics

```typescript
// Get activity for a date range
const activity = await client.analytics.getActivity({
	studentId: 'student-id',
	startDate: '2025-01-01',
	endDate: '2025-01-31',
	timezone: 'America/New_York',
})

console.log(activity.facts)
console.log(activity.factsByApp)

// Get weekly facts
const facts = await client.analytics.getWeeklyFacts({
	studentId: 'student-id',
	weekDate: '2025-01-06',
})

// Get enrollment facts for a single course enrollment
const enrollmentFacts = await client.analytics.getEnrollmentFacts({
	enrollmentId: 'enrollment-id',
})

console.log(enrollmentFacts.facts)
console.log(enrollmentFacts.factsByApp)

// Get highest grade mastered
const gradeData = await client.analytics.getHighestGradeMastered('student-id', 'math')
```

### Authentication

The client handles OAuth2 token management automatically:

```typescript
// Environment mode (recommended for Timeback APIs)
const client = new EdubridgeClient({
	env: 'staging', // or 'production'
	auth: {
		clientId: 'xxx',
		clientSecret: 'xxx',
	},
})

// Explicit mode (custom API)
const client = new EdubridgeClient({
	baseUrl: 'https://api.example.com',
	auth: {
		clientId: 'xxx',
		clientSecret: 'xxx',
		authUrl: 'https://auth.example.com/oauth2/token',
	},
})
```

Tokens are cached and refreshed automatically before expiry.

### Error Handling

```typescript
import { EdubridgeError, NotFoundError } from '@timeback/edubridge'

try {
	await client.enrollments.getByUser('invalid-id')
} catch (error) {
	if (error instanceof NotFoundError) {
		console.log('User not found')
	} else if (error instanceof EdubridgeError) {
		console.log(error.statusCode) // HTTP status
		console.log(error.message)
	}
}
```

## Configuration

```typescript
new EdubridgeClient({
	// Environment mode (Timeback APIs)
	env: 'staging' | 'production',
	auth: {
		clientId: string,
		clientSecret: string,
	},

	// OR Explicit mode (custom API)
	baseUrl: string,
	auth: {
		clientId: string,
		clientSecret: string,
		authUrl: string,
	},

	// Optional
	fetch?: typeof fetch, // Custom fetch implementation
	timeout?: number, // Request timeout in ms (default: 30000)

	// Internal (for composition with @timeback/core)
	transport?: Transport, // Shared HTTP transport
})
```

## Debug Mode

Enable debug logging by setting `DEBUG=1` or `DEBUG=true`:

```bash
DEBUG=1 bun run my-script.ts
```

This outputs detailed logs for HTTP requests and authentication:

```bash
[2025-01-15T10:30:00.000Z] DEBUG [edubridge:auth] Fetching new access token...
[2025-01-15T10:30:00.500Z] DEBUG [edubridge:auth] Token acquired (500ms, expires in 3600s)
[2025-01-15T10:30:00.501Z] DEBUG [edubridge:http] → POST https://api.example.com/edubridge/enrollments/enroll/...
[2025-01-15T10:30:00.800Z] DEBUG [edubridge:http] ← 201 Created (299ms)
```
