package main

// Cipher model: decrypt/encrypt for the operations the lane uses
// (secure notes with hidden fields; login items for password fields).

import (
	"encoding/json"
	"fmt"
	"strings"
)

// encStr handles empty/unset gracefully.
type encFunc func(string) (string, error)

type CipherField struct {
	Type  int     `json:"type"` // 0=text, 1=hidden, 2=boolean (Bitwarden field type)
	Name  *string `json:"name,omitempty"`
	Value *string `json:"value,omitempty"`
}

type CipherLogin struct {
	Username *string `json:"username,omitempty"`
	Password *string `json:"password,omitempty"`
	URIs     []any   `json:"uris,omitempty"`
}

type cipherRaw struct {
	ID             string                 `json:"id"`
	OrganizationID *string                `json:"organizationId"`
	Type           int                    `json:"type"` // 1=login, 2=secureNote
	Name           string                 `json:"name"`
	Notes          *string                `json:"notes"`
	Fields         []CipherField          `json:"fields,omitempty"`
	Key            *string                `json:"key,omitempty"`
	Login          *CipherLogin           `json:"login,omitempty"`
	SecureNote     map[string]any         `json:"secureNote,omitempty"`
	DeletedDate    *string                `json:"deletedDate,omitempty"`
	Extra          map[string]interface{} `json:"-"`
}

// cipherKeyFor returns the 64B key to use for a cipher's data.
func cipherKeyFor(userKey []byte, encKey *string) ([]byte, error) {
	if encKey == nil || *encKey == "" {
		return userKey, nil
	}
	es, err := ParseEncString(*encKey)
	if err != nil {
		return nil, err
	}
	return symDecrypt(userKey, es)
}

func decStr(userKey []byte, cipherKey []byte, s *string) (string, error) {
	if s == nil || *s == "" {
		return "", nil
	}
	es, err := ParseEncString(*s)
	if err != nil {
		return "", err
	}
	pt, err := symDecrypt(cipherKey, es)
	if err != nil {
		return "", err
	}
	return string(pt), nil
}

func encStr(userKey []byte, s string) (string, error) {
	if s == "" {
		return "", nil
	}
	return symEncrypt(userKey, []byte(s))
}

// PlainField is a decrypted custom field.
type PlainField struct {
	Type  int    `json:"type"`
	Name  string `json:"name"`
	Value string `json:"value"`
}

// PlainCipher is a decrypted item view.
type PlainCipher struct {
	ID     string       `json:"id"`
	Name   string       `json:"name"`
	Notes  string       `json:"notes"`
	Type   int          `json:"type"`
	Fields []PlainField `json:"fields"`
	Login  struct {
		Username string `json:"username"`
		Password string `json:"password"`
	} `json:"login"`
	FolderID string   `json:"folderId"`
	URIs     []string `json:"uris"`
}

// DecryptCipher converts a raw sync cipher into PlainCipher.
func DecryptCipher(userKey []byte, raw []byte) (*PlainCipher, error) {
	var cr cipherRaw
	if err := json.Unmarshal(raw, &cr); err != nil {
		return nil, err
	}
	if cr.DeletedDate != nil {
		return nil, fmt.Errorf("deleted")
	}
	ck, err := cipherKeyFor(userKey, cr.Key)
	if err != nil {
		return nil, err
	}
	pc := &PlainCipher{ID: cr.ID, Type: cr.Type}
	if pc.Name, err = decStr(userKey, ck, &cr.Name); err != nil {
		return nil, fmt.Errorf("name: %w", err)
	}
	if cr.Notes != nil {
		if pc.Notes, err = decStr(userKey, ck, cr.Notes); err != nil {
			return nil, fmt.Errorf("notes: %w", err)
		}
	}
	for _, f := range cr.Fields {
		pf := PlainField{Type: f.Type}
		if pf.Name, err = decStr(userKey, ck, f.Name); err != nil {
			continue
		}
		if pf.Value, err = decStr(userKey, ck, f.Value); err != nil {
			continue
		}
		pc.Fields = append(pc.Fields, pf)
	}
	if cr.Login != nil {
		if cr.Login.Username != nil {
			pc.Login.Username, _ = decStr(userKey, ck, cr.Login.Username)
		}
		if cr.Login.Password != nil {
			pc.Login.Password, _ = decStr(userKey, ck, cr.Login.Password)
		}
		for _, u := range cr.Login.URIs {
			var um map[string]any
			um, _ = u.(map[string]any)
			if um != nil {
				if us, ok := um["uri"].(string); ok {
					v, err := decStr(userKey, ck, &us)
					if err == nil {
						pc.URIs = append(pc.URIs, v)
					}
				}
			}
		}
	}
	return pc, nil
}

// BuildSecureNoteJSON produces an encrypted create/update payload for a
// secure-note item with hidden fields (type 1).
func BuildSecureNoteJSON(userKey []byte, name, folderID, notes string, fields []PlainField, existing *cipherRaw) (map[string]any, error) {
	nameEnc, err := encStr(userKey, name)
	if err != nil {
		return nil, err
	}
	notesEnc, err := encStr(userKey, notes)
	if err != nil {
		return nil, err
	}
	var encFields []map[string]any
	for _, f := range fields {
		fn, err := encStr(userKey, f.Name)
		if err != nil {
			return nil, err
		}
		fv, err := encStr(userKey, f.Value)
		if err != nil {
			return nil, err
		}
		encFields = append(encFields, map[string]any{
			"type":  f.Type,
			"name":  fn,
			"value": fv,
		})
	}
	payload := map[string]any{
		"type":       2,
		"name":       nameEnc,
		"notes":      notesEnc,
		"fields":     encFields,
		"secureNote": map[string]any{"type": 0},
	}
	if folderID != "" {
		payload["folderId"] = folderID
	}
	if existing != nil {
		payload["id"] = existing.ID
		if existing.OrganizationID != nil {
			payload["organizationId"] = existing.OrganizationID
		}
	}
	return payload, nil
}

// parseEnvFields parses KEY=VALUE lines (quoted or bare) into hidden fields.
func parseEnvFields(text string) []PlainField {
	var out []PlainField
	for _, line := range strings.Split(text, "\n") {
		line = strings.TrimSpace(line)
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		eq := strings.Index(line, "=")
		if eq <= 0 {
			continue
		}
		k := line[:eq]
		v := line[eq+1:]
		v = strings.Trim(v, "'\"")
		out = append(out, PlainField{Type: 1, Name: k, Value: v})
	}
	return out
}

// BuildLoginJSON produces an encrypted login-item payload: username/password
// as first-class login fields, URIs, and the remaining keys as custom fields
// (hidden for secrets, text for URLs/IDs).
func BuildLoginJSON(userKey []byte, name, folderID, username, password string, uris []string, fields []PlainField, existing *cipherRaw) (map[string]any, error) {
	nameEnc, err := encStr(userKey, name)
	if err != nil {
		return nil, err
	}
	login := map[string]any{}
	if username != "" {
		u, err := encStr(userKey, username)
		if err != nil {
			return nil, err
		}
		login["username"] = u
	}
	if password != "" {
		p, err := encStr(userKey, password)
		if err != nil {
			return nil, err
		}
		login["password"] = p
	}
	if len(uris) > 0 {
		var list []map[string]any
		for _, u := range uris {
			ue, err := encStr(userKey, u)
			if err != nil {
				return nil, err
			}
			list = append(list, map[string]any{"uri": ue})
		}
		login["uris"] = list
	}
	var encFields []map[string]any
	for _, f := range fields {
		fn, err := encStr(userKey, f.Name)
		if err != nil {
			return nil, err
		}
		fv, err := encStr(userKey, f.Value)
		if err != nil {
			return nil, err
		}
		encFields = append(encFields, map[string]any{"type": f.Type, "name": fn, "value": fv})
	}
	payload := map[string]any{"type": 1, "name": nameEnc, "login": login}
	if len(encFields) > 0 {
		payload["fields"] = encFields
	}
	if folderID != "" {
		payload["folderId"] = folderID
	}
	if existing != nil {
		payload["id"] = existing.ID
		if existing.OrganizationID != nil {
			payload["organizationId"] = existing.OrganizationID
		}
	}
	return payload, nil
}
