package database

import (
	"context"
	"database/sql"
	"time"

	"github.com/google/uuid"
)

type DB struct {
	*sql.DB
}

func New(db *sql.DB) *DB {
	return &DB{DB: db}
}

type User struct {
	ID          uuid.UUID
	Email       string
	DisplayName string
	CreatedAt   time.Time
	LastLoginAt *time.Time
}

type Session struct {
	ID        uuid.UUID
	UserID    uuid.UUID
	ExpiresAt time.Time
	RevokedAt *time.Time
}

type Organization struct {
	ID        uuid.UUID
	Name      string
	Slug      string
	CreatedAt time.Time
}

type Membership struct {
	UserID    uuid.UUID
	OrgID     uuid.UUID
	Role      string
	CreatedAt time.Time
}

type Activity struct {
	ID        uuid.UUID
	UserID    uuid.UUID
	OrgID     uuid.UUID
	FileID    *string
	Action    string
	Metadata  map[string]interface{}
	Timestamp time.Time
}

func (db *DB) GetOrCreateUser(ctx context.Context, sub, email, name string) (*User, error) {
	var user User
	err := db.QueryRowContext(ctx, `
		INSERT INTO users (id, email, display_name)
		VALUES (gen_random_uuid(), $1, $2)
		ON CONFLICT (email) DO UPDATE SET
			display_name = EXCLUDED.display_name,
			last_login_at = NOW()
		RETURNING id, email, display_name, created_at, last_login_at
	`, email, name).Scan(&user.ID, &user.Email, &user.DisplayName, &user.CreatedAt, &user.LastLoginAt)
	if err != nil {
		return nil, err
	}
	return &user, nil
}

func (db *DB) CreateSession(ctx context.Context, userID uuid.UUID, expiresAt time.Time) (*Session, error) {
	var session Session
	err := db.QueryRowContext(ctx, `
		INSERT INTO sessions (user_id, expires_at)
		VALUES ($1, $2)
		RETURNING id, user_id, expires_at, revoked_at
	`, userID, expiresAt).Scan(&session.ID, &session.UserID, &session.ExpiresAt, &session.RevokedAt)
	if err != nil {
		return nil, err
	}
	return &session, nil
}

func (db *DB) GetSession(ctx context.Context, sessionID uuid.UUID) (*Session, error) {
	var session Session
	err := db.QueryRowContext(ctx, `
		SELECT id, user_id, expires_at, revoked_at
		FROM sessions
		WHERE id = $1
	`, sessionID).Scan(&session.ID, &session.UserID, &session.ExpiresAt, &session.RevokedAt)
	if err != nil {
		return nil, err
	}
	return &session, nil
}

func (db *DB) GetUserOrganizations(ctx context.Context, userID uuid.UUID) ([]Organization, error) {
	rows, err := db.QueryContext(ctx, `
		SELECT o.id, o.name, o.slug, o.created_at
		FROM organizations o
		JOIN memberships m ON o.id = m.org_id
		WHERE m.user_id = $1
	`, userID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var orgs []Organization
	for rows.Next() {
		var org Organization
		if err := rows.Scan(&org.ID, &org.Name, &org.Slug, &org.CreatedAt); err != nil {
			return nil, err
		}
		orgs = append(orgs, org)
	}
	return orgs, rows.Err()
}

func (db *DB) GetUserMembership(ctx context.Context, userID, orgID uuid.UUID) (*Membership, error) {
	var membership Membership
	err := db.QueryRowContext(ctx, `
		SELECT user_id, org_id, role, created_at
		FROM memberships
		WHERE user_id = $1 AND org_id = $2
	`, userID, orgID).Scan(&membership.UserID, &membership.OrgID, &membership.Role, &membership.CreatedAt)
	if err != nil {
		return nil, err
	}
	return &membership, nil
}

func (db *DB) CreateOrg(ctx context.Context, name, slug string) (*Organization, error) {
	var org Organization
	err := db.QueryRowContext(ctx, `
		INSERT INTO organizations (name, slug)
		VALUES ($1, $2)
		RETURNING id, name, slug, created_at
	`, name, slug).Scan(&org.ID, &org.Name, &org.Slug, &org.CreatedAt)
	if err != nil {
		return nil, err
	}
	return &org, nil
}

func (db *DB) AddMembership(ctx context.Context, userID, orgID uuid.UUID, role string) error {
	_, err := db.ExecContext(ctx, `
		INSERT INTO memberships (user_id, org_id, role)
		VALUES ($1, $2, $3)
	`, userID, orgID, role)
	return err
}

func (db *DB) LogActivity(ctx context.Context, userID, orgID uuid.UUID, fileID *string, action string, metadata map[string]interface{}) error {
	_, err := db.ExecContext(ctx, `
		INSERT INTO activities (user_id, org_id, file_id, action, metadata)
		VALUES ($1, $2, $3, $4, $5)
	`, userID, orgID, fileID, action, metadata)
	return err
}

func (db *DB) GetOrgActivities(ctx context.Context, orgID uuid.UUID, limit int) ([]Activity, error) {
	rows, err := db.QueryContext(ctx, `
		SELECT id, user_id, org_id, file_id, action, metadata, timestamp
		FROM activities
		WHERE org_id = $1
		ORDER BY timestamp DESC
		LIMIT $2
	`, orgID, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var activities []Activity
	for rows.Next() {
		var a Activity
		err := rows.Scan(&a.ID, &a.UserID, &a.OrgID, &a.FileID, &a.Action, &a.Metadata, &a.Timestamp)
		if err != nil {
			return nil, err
		}
		activities = append(activities, a)
	}
	return activities, rows.Err()
}

func (db *DB) GetOrgMembers(ctx context.Context, orgID uuid.UUID) ([]Membership, error) {
	rows, err := db.QueryContext(ctx, `
		SELECT user_id, org_id, role, created_at
		FROM memberships
		WHERE org_id = $1
	`, orgID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var memberships []Membership
	for rows.Next() {
		var m Membership
		err := rows.Scan(&m.UserID, &m.OrgID, &m.Role, &m.CreatedAt)
		if err != nil {
			return nil, err
		}
		memberships = append(memberships, m)
	}
	return memberships, rows.Err()
}

func (db *DB) UpdateMemberRole(ctx context.Context, orgID, userID uuid.UUID, role string) error {
	_, err := db.ExecContext(ctx, `
		UPDATE memberships
		SET role = $1
		WHERE org_id = $2 AND user_id = $3
	`, role, orgID, userID)
	return err
}
