package lib

import (
	"archive/zip"
	"bytes"
	"encoding/csv"
	"encoding/json"
	"io"
	"strconv"
	"strings"
	"time"
)

// DetectAndParse attempts to parse known password manager formats directly.
// Returns (entries, true) if format recognized, (nil, false) if unknown.
func DetectAndParse(content []byte) ([]VaultData, bool) {
	// If it's a zip, extract the first JSON file and parse that
	if content, ok := extractFromZip(content); ok {
		return DetectAndParse(content)
	}
	// Strip UTF-8 BOM if present (common in browser CSV exports)
	content = bytes.TrimPrefix(content, []byte{0xEF, 0xBB, 0xBF})
	// Try Bitwarden/generic JSON array with "items" key
	if entries, ok := parseBitwardenJSON(content); ok {
		AutoL2Fields(entries)
		return entries, true
	}
	// Try Proton Pass JSON
	if entries, ok := parseProtonJSON(content); ok {
		AutoL2Fields(entries)
		return entries, true
	}
	// Try Chrome/Firefox CSV
	if entries, ok := parseBrowserCSV(content); ok {
		AutoL2Fields(entries)
		return entries, true
	}
	return nil, false
}

// extractFromZip tries to read content as a zip archive and returns the first
// JSON or CSV file found inside. Used for Proton Pass exports (zip containing JSON).
func extractFromZip(content []byte) ([]byte, bool) {
	r, err := zip.NewReader(bytes.NewReader(content), int64(len(content)))
	if err != nil {
		return nil, false
	}
	for _, f := range r.File {
		name := strings.ToLower(f.Name)
		if strings.HasSuffix(name, ".json") || strings.HasSuffix(name, ".csv") {
			rc, err := f.Open()
			if err != nil {
				continue
			}
			data, err := io.ReadAll(rc)
			rc.Close()
			if err != nil {
				continue
			}
			return data, true
		}
	}
	return nil, false
}

// readCSV parses content with the given delimiter. Returns (records, true) if valid with ≥2 rows.
func readCSV(content []byte, delim rune) ([][]string, bool) {
	r := csv.NewReader(bytes.NewReader(content))
	r.Comma = delim
	r.LazyQuotes = true
	r.TrimLeadingSpace = true
	records, err := r.ReadAll()
	if err != nil || len(records) < 2 {
		return nil, false
	}
	// Sanity: header should have at least 3 columns
	if len(records[0]) < 3 {
		return nil, false
	}
	return records, true
}

// --- Chrome CSV ---
// Columns: name,url,username,password  (Chrome)
// Columns: url,username,password,httpRealm,formActionOrigin,guid,timeCreated,timeLastUsed,timePasswordChanged  (Firefox)
func parseBrowserCSV(content []byte) ([]VaultData, bool) {
	// Try comma first, then semicolon (European locale exports)
	records, ok := readCSV(content, ',')
	if !ok {
		records, ok = readCSV(content, ';')
	}
	if !ok {
		return nil, false
	}
	header := records[0]
	colIdx := map[string]int{}
	for i, h := range header {
		colIdx[strings.ToLower(strings.TrimSpace(h))] = i
	}

	// Detect Chrome: name, url, username, password
	// Detect Firefox: url, username, password (+ extras)
	urlCol, hasURL := colIdx["url"]
	userCol, hasUser := colIdx["username"]
	passCol, hasPass := colIdx["password"]
	nameCol, hasName := colIdx["name"]
	// Firefox: timePasswordChanged (Unix microseconds)
	timeChangedCol, hasTimeChanged := colIdx["timepasswordchanged"]

	if !hasURL || !hasUser || !hasPass {
		return nil, false
	}

	var entries []VaultData
	for _, row := range records[1:] {
		if len(row) <= urlCol || len(row) <= passCol {
			continue
		}
		title := ""
		if hasName && len(row) > nameCol {
			title = row[nameCol]
		}
		if title == "" {
			title = row[urlCol]
		}
		entry := VaultData{
			Title: title,
			Type:  "credential",
			Fields: []VaultField{
				{Label: "Username", Value: row[userCol], Kind: "text"},
				{Label: "Password", Value: row[passCol], Kind: "password"},
			},
		}
		if row[urlCol] != "" {
			entry.URLs = []string{row[urlCol]}
		}
		// Firefox stores timestamps as Unix microseconds
		if hasTimeChanged && len(row) > timeChangedCol && row[timeChangedCol] != "" {
			if us, err := strconv.ParseInt(row[timeChangedCol], 10, 64); err == nil && us > 0 {
				entry.SourceModified = us / 1_000_000 // microseconds → seconds
			}
		}
		entries = append(entries, entry)
	}
	return entries, len(entries) > 0
}

// --- Bitwarden JSON ---
type bitwardenExport struct {
	Items []bitwardenItem `json:"items"`
}
type bitwardenItem struct {
	Name         string             `json:"name"`
	Type         int                `json:"type"` // 1=login, 2=note, 3=card, 4=identity
	Notes        string             `json:"notes"`
	RevisionDate string             `json:"revisionDate"` // RFC3339
	Login        *bitwardenLogin    `json:"login"`
	Card         *bitwardenCard     `json:"card"`
	Identity     *bitwardenIdentity `json:"identity"`
}
type bitwardenLogin struct {
	Username string `json:"username"`
	Password string `json:"password"`
	Totp     string `json:"totp"`
	URIs     []struct{ URI string `json:"uri"` } `json:"uris"`
}
type bitwardenCard struct {
	CardholderName string `json:"cardholderName"`
	Number         string `json:"number"`
	ExpMonth       string `json:"expMonth"`
	ExpYear        string `json:"expYear"`
	Code           string `json:"code"`
}
type bitwardenIdentity struct {
	FirstName string `json:"firstName"`
	LastName  string `json:"lastName"`
	Email     string `json:"email"`
	Phone     string `json:"phone"`
	Address1  string `json:"address1"`
	City      string `json:"city"`
	State     string `json:"state"`
	PostalCode string `json:"postalCode"`
	Country   string `json:"country"`
}

func parseBitwardenJSON(content []byte) ([]VaultData, bool) {
	var bw bitwardenExport
	if err := json.Unmarshal(content, &bw); err != nil || len(bw.Items) == 0 {
		return nil, false
	}
	var entries []VaultData
	for _, item := range bw.Items {
		vd := VaultData{Title: item.Name, Notes: item.Notes}
		switch item.Type {
		case 1: // login
			vd.Type = "credential"
			if item.Login != nil {
				vd.Fields = append(vd.Fields, VaultField{Label: "Username", Value: item.Login.Username, Kind: "text"})
				vd.Fields = append(vd.Fields, VaultField{Label: "Password", Value: item.Login.Password, Kind: "password"})
				if item.Login.Totp != "" {
					vd.Fields = append(vd.Fields, VaultField{Label: "TOTP Seed", Value: item.Login.Totp, Kind: "totp"})
				}
				for _, u := range item.Login.URIs {
					if u.URI != "" {
						vd.URLs = append(vd.URLs, u.URI)
					}
				}
			}
		case 2: // note
			vd.Type = "note"
			vd.Fields = append(vd.Fields, VaultField{Label: "Content", Value: item.Notes, Kind: "text"})
			vd.Notes = ""
		case 3: // card
			vd.Type = "card"
			if item.Card != nil {
				vd.Fields = append(vd.Fields, VaultField{Label: "Cardholder", Value: item.Card.CardholderName, Kind: "text"})
				vd.Fields = append(vd.Fields, VaultField{Label: "Number", Value: item.Card.Number, Kind: "text", L2: true})
				vd.Fields = append(vd.Fields, VaultField{Label: "CVV", Value: item.Card.Code, Kind: "text", L2: true})
				vd.Fields = append(vd.Fields, VaultField{Label: "Expiry", Value: item.Card.ExpMonth + "/" + item.Card.ExpYear, Kind: "text"})
			}
		case 4: // identity
			vd.Type = "identity"
			if item.Identity != nil {
				id := item.Identity
				addField := func(label, value string) {
					if value != "" {
						vd.Fields = append(vd.Fields, VaultField{Label: label, Value: value, Kind: "text"})
					}
				}
				addField("First Name", id.FirstName)
				addField("Last Name", id.LastName)
				addField("Email", id.Email)
				addField("Phone", id.Phone)
				addField("Address", id.Address1)
				addField("City", id.City)
				addField("State", id.State)
				addField("ZIP", id.PostalCode)
				addField("Country", id.Country)
			}
		}
		// Parse revisionDate → SourceModified
		if item.RevisionDate != "" {
			if t, err := time.Parse(time.RFC3339, item.RevisionDate); err == nil {
				vd.SourceModified = t.Unix()
			}
		}
		entries = append(entries, vd)
	}
	return entries, len(entries) > 0
}

// --- Proton Pass JSON ---
// Proton nests everything under item.data: type, metadata, content are all inside data.
type protonExport struct {
	Vaults map[string]protonVault `json:"vaults"`
}
type protonVault struct {
	Items []protonItem `json:"items"`
}
type protonItem struct {
	Data       protonItemData `json:"data"`
	ModifyTime int64          `json:"modifyTime"` // Unix seconds
	CreateTime int64          `json:"createTime"`
}
type protonItemData struct {
	Type     string       `json:"type"`
	Metadata protonMeta   `json:"metadata"`
	Content  protonContent `json:"content"`
}
type protonMeta struct {
	Name string `json:"name"`
	Note string `json:"note"`
}
type protonContent struct {
	// login
	ItemUsername string   `json:"itemUsername"`
	ItemEmail   string   `json:"itemEmail"`
	Password    string   `json:"password"`
	Urls        []string `json:"urls"`
	TOTPUri     string   `json:"totpUri"`
	// card
	CardholderName     string `json:"cardholderName"`
	Number             string `json:"number"`
	VerificationNumber string `json:"verificationNumber"`
	ExpirationDate     string `json:"expirationDate"`
	// identity
	FullName string `json:"fullName"`
	Email    string `json:"email"`
	Phone    string `json:"phoneNumber"`
	// alias
	AliasEmail string `json:"aliasEmail"`
}

func parseProtonJSON(content []byte) ([]VaultData, bool) {
	var pe protonExport
	if err := json.Unmarshal(content, &pe); err != nil || len(pe.Vaults) == 0 {
		return nil, false
	}
	var entries []VaultData
	for _, vault := range pe.Vaults {
		for _, item := range vault.Items {
			d := item.Data
			vd := VaultData{
				Title: d.Metadata.Name,
				Notes: d.Metadata.Note,
				URLs:  d.Content.Urls,
			}
			// Pick best username: itemUsername, itemEmail, or email
			username := d.Content.ItemUsername
			if username == "" {
				username = d.Content.ItemEmail
			}
			switch d.Type {
			case "login":
				vd.Type = "credential"
				vd.Fields = append(vd.Fields, VaultField{Label: "Username", Value: username, Kind: "text"})
				vd.Fields = append(vd.Fields, VaultField{Label: "Password", Value: d.Content.Password, Kind: "password"})
				if d.Content.TOTPUri != "" {
					vd.Fields = append(vd.Fields, VaultField{Label: "TOTP Seed", Value: d.Content.TOTPUri, Kind: "totp"})
				}
			case "creditCard":
				vd.Type = "card"
				vd.Fields = append(vd.Fields, VaultField{Label: "Cardholder", Value: d.Content.CardholderName, Kind: "text"})
				vd.Fields = append(vd.Fields, VaultField{Label: "Number", Value: d.Content.Number, Kind: "text", L2: true})
				vd.Fields = append(vd.Fields, VaultField{Label: "CVV", Value: d.Content.VerificationNumber, Kind: "text", L2: true})
				vd.Fields = append(vd.Fields, VaultField{Label: "Expiry", Value: d.Content.ExpirationDate, Kind: "text"})
			case "identity":
				vd.Type = "identity"
				addF := func(l, v string) {
					if v != "" {
						vd.Fields = append(vd.Fields, VaultField{Label: l, Value: v, Kind: "text"})
					}
				}
				addF("Full Name", d.Content.FullName)
				addF("Email", d.Content.Email)
				addF("Phone", d.Content.Phone)
			case "alias":
				continue // Proton-specific email alias — not a vault entry
			case "note":
				vd.Type = "note"
				vd.Fields = append(vd.Fields, VaultField{Label: "Content", Value: d.Metadata.Note, Kind: "text"})
				vd.Notes = ""
			default:
				vd.Type = "note"
			}
			if item.ModifyTime > 0 {
				vd.SourceModified = item.ModifyTime
			}
			entries = append(entries, vd)
		}
	}
	return entries, len(entries) > 0
}
