package db

import (
	"database/sql"
	"errors"
)

// CreatePGPIdentity adds a new PGP identity for a mailbox — a mailbox may hold
// several at once (see esrv_mailbox_pgp_identities in schema.go).
func (d *DB) CreatePGPIdentity(mailboxID int64, label, email, fingerprint, publicKeyArmor, privateKeyArmor string) (int64, error) {
	res, err := d.Exec(`
		INSERT INTO esrv_mailbox_pgp_identities (mailbox_id, label, email, fingerprint, public_key_armor, private_key_armor)
		VALUES (?, ?, ?, ?, ?, ?)
	`, mailboxID, label, email, fingerprint, publicKeyArmor, privateKeyArmor)
	if err != nil {
		return 0, err
	}
	return res.LastInsertId()
}

// ListPGPIdentities returns a mailbox's PGP identities, most recent first.
func (d *DB) ListPGPIdentities(mailboxID int64) ([]MailboxPGPIdentity, error) {
	rows, err := d.Query(`SELECT id, mailbox_id, label, email, fingerprint, public_key_armor, private_key_armor, created_at
		FROM esrv_mailbox_pgp_identities WHERE mailbox_id = ? ORDER BY created_at DESC, id DESC`, mailboxID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var out []MailboxPGPIdentity
	for rows.Next() {
		var id MailboxPGPIdentity
		if err := rows.Scan(&id.ID, &id.MailboxID, &id.Label, &id.Email, &id.Fingerprint, &id.PublicKeyArmor, &id.PrivateKeyArmor, &id.CreatedAt); err != nil {
			return nil, err
		}
		out = append(out, id)
	}
	return out, rows.Err()
}

// GetPGPIdentity returns nil, nil if no such identity exists for this mailbox —
// scoped to mailboxID so one mailbox owner can't reach another's identity by
// guessing its ID.
func (d *DB) GetPGPIdentity(mailboxID, identityID int64) (*MailboxPGPIdentity, error) {
	row := d.QueryRow(`SELECT id, mailbox_id, label, email, fingerprint, public_key_armor, private_key_armor, created_at
		FROM esrv_mailbox_pgp_identities WHERE mailbox_id = ? AND id = ?`, mailboxID, identityID)
	var id MailboxPGPIdentity
	if err := row.Scan(&id.ID, &id.MailboxID, &id.Label, &id.Email, &id.Fingerprint, &id.PublicKeyArmor, &id.PrivateKeyArmor, &id.CreatedAt); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, nil
		}
		return nil, err
	}
	return &id, nil
}

// DeletePGPIdentity removes one identity, scoped to mailboxID.
func (d *DB) DeletePGPIdentity(mailboxID, identityID int64) error {
	_, err := d.Exec(`DELETE FROM esrv_mailbox_pgp_identities WHERE mailbox_id = ? AND id = ?`, mailboxID, identityID)
	return err
}

// UpsertPGPContact adds a contact's PGP public key, replacing any existing key
// already on file for that email (e.g. after the contact rotates their key).
func (d *DB) UpsertPGPContact(mailboxID int64, email, label, fingerprint, publicKeyArmor string) error {
	_, err := d.Exec(`
		INSERT INTO esrv_mailbox_pgp_contacts (mailbox_id, email, label, fingerprint, public_key_armor)
		VALUES (?, ?, ?, ?, ?)
		ON CONFLICT(mailbox_id, email) DO UPDATE SET label = excluded.label, fingerprint = excluded.fingerprint, public_key_armor = excluded.public_key_armor
	`, mailboxID, email, label, fingerprint, publicKeyArmor)
	return err
}

// GetPGPContact returns nil, nil if no key is on file for that email.
func (d *DB) GetPGPContact(mailboxID int64, email string) (*MailboxPGPContact, error) {
	row := d.QueryRow(`SELECT id, mailbox_id, email, label, public_key_armor, fingerprint, created_at FROM esrv_mailbox_pgp_contacts WHERE mailbox_id = ? AND email = ?`, mailboxID, email)
	var c MailboxPGPContact
	if err := row.Scan(&c.ID, &c.MailboxID, &c.Email, &c.Label, &c.PublicKeyArmor, &c.Fingerprint, &c.CreatedAt); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, nil
		}
		return nil, err
	}
	return &c, nil
}

// GetPGPContactByID returns nil, nil if no such contact exists for this mailbox —
// scoped to mailboxID so one mailbox owner can't reach another's contact by guessing
// its ID. Used by compose's recipient-key picker, which selects contacts by ID
// rather than matching a To/Cc/Bcc address against GetPGPContact's stored email.
func (d *DB) GetPGPContactByID(mailboxID, contactID int64) (*MailboxPGPContact, error) {
	row := d.QueryRow(`SELECT id, mailbox_id, email, label, public_key_armor, fingerprint, created_at FROM esrv_mailbox_pgp_contacts WHERE mailbox_id = ? AND id = ?`, mailboxID, contactID)
	var c MailboxPGPContact
	if err := row.Scan(&c.ID, &c.MailboxID, &c.Email, &c.Label, &c.PublicKeyArmor, &c.Fingerprint, &c.CreatedAt); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, nil
		}
		return nil, err
	}
	return &c, nil
}

// ListPGPContacts returns a mailbox's collected contact keys, alphabetical by email.
func (d *DB) ListPGPContacts(mailboxID int64) ([]MailboxPGPContact, error) {
	rows, err := d.Query(`SELECT id, mailbox_id, email, label, public_key_armor, fingerprint, created_at FROM esrv_mailbox_pgp_contacts WHERE mailbox_id = ? ORDER BY email`, mailboxID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var out []MailboxPGPContact
	for rows.Next() {
		var c MailboxPGPContact
		if err := rows.Scan(&c.ID, &c.MailboxID, &c.Email, &c.Label, &c.PublicKeyArmor, &c.Fingerprint, &c.CreatedAt); err != nil {
			return nil, err
		}
		out = append(out, c)
	}
	return out, rows.Err()
}

// DeletePGPContact removes one contact key, scoped to mailboxID so one mailbox
// owner can't delete another's contact by guessing its ID.
func (d *DB) DeletePGPContact(mailboxID, contactID int64) error {
	_, err := d.Exec(`DELETE FROM esrv_mailbox_pgp_contacts WHERE mailbox_id = ? AND id = ?`, mailboxID, contactID)
	return err
}
