package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"time"
)

const (
	BaseURL   = "https://hiring.cafe"
	SearchURL = BaseURL + "/api/search-jobs"
	UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
	PageSize  = 20
)

// Client handles API requests to hiring.cafe
type Client struct {
	httpClient *http.Client
}

// NewClient creates a new API client
func NewClient() *Client {
	return &Client{
		httpClient: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// SearchRequest represents the search request payload (based on RSSHub implementation)
type SearchRequest struct {
	Size        int         `json:"size"`
	Page        int         `json:"page"`
	SearchState SearchState `json:"searchState"`
}

// SearchState contains search parameters
type SearchState struct {
	SearchQuery string `json:"searchQuery"`
	SortBy      string `json:"sortBy"` // date, default, compensation_desc, experience_asc
}

// SearchResponse represents the API response
type SearchResponse struct {
	Results []JobResult `json:"results"`
	Total   int         `json:"total"`
}

// JobResult represents a job result from the API
type JobResult struct {
	ID               string           `json:"id"`
	ApplyURL         string           `json:"apply_url"`
	JobInformation   JobInformation   `json:"job_information"`
	ProcessedJobData ProcessedJobData `json:"v5_processed_job_data"`
}

// JobInformation contains basic job info
type JobInformation struct {
	Title       string `json:"title"`
	Description string `json:"description"`
}

// ProcessedJobData contains processed job details
type ProcessedJobData struct {
	CompanyName                string   `json:"company_name"`
	IsCompensationTransparent  bool     `json:"is_compensation_transparent"`
	YearlyMinCompensation      *float64 `json:"yearly_min_compensation"`
	YearlyMaxCompensation      *float64 `json:"yearly_max_compensation"`
	WorkplaceType              string   `json:"workplace_type"`
	RequirementsSummary        string   `json:"requirements_summary"`
	JobCategory                string   `json:"job_category"`
	RoleActivities             []string `json:"role_activities"`
	FormattedWorkplaceLocation string   `json:"formatted_workplace_location"`
	EstimatedPublishDateMillis string   `json:"estimated_publish_date_millis"`
}

// Job is a simplified job structure for external use
type Job struct {
	ID               string
	ApplyURL         string
	Title            string
	Description      string
	Company          string
	CompanyURL       string
	Location         string
	Remote           bool
	Salary           string
	PostedAt         time.Time
	DescriptionClean string
}

// SearchFilters contains user-configurable search filters
type SearchFilters struct {
	Query         string
	Country       string
	Remote        bool
	Hybrid        bool
	Onsite        bool
	FullTime      bool
	PartTime      bool
	Contract      bool
	Internship    bool
	EntryLevel    bool
	MidLevel      bool
	SeniorLevel   bool
	DateRangeDays int
	MaxPages      int
}

// DefaultFilters returns default search filters
func DefaultFilters() SearchFilters {
	return SearchFilters{
		Query:         "",
		Country:       "United States",
		Remote:        true,
		Hybrid:        true,
		Onsite:        true,
		FullTime:      true,
		PartTime:      false,
		Contract:      false,
		Internship:    false,
		EntryLevel:    true,
		MidLevel:      true,
		SeniorLevel:   false,
		DateRangeDays: 30,
		MaxPages:      10,
	}
}

// Search performs a job search with the given filters
func (c *Client) Search(filters SearchFilters) ([]Job, error) {
	var allJobs []Job
	page := 1

	for {
		jobs, totalCount, err := c.searchPage(filters, page)
		if err != nil {
			return nil, fmt.Errorf("failed to search page %d: %w", page, err)
		}

		allJobs = append(allJobs, jobs...)

		// Check if we've fetched all jobs or reached max pages
		if len(allJobs) >= totalCount || page >= filters.MaxPages || len(jobs) == 0 {
			break
		}

		page++
		// Rate limiting - be nice to the API
		time.Sleep(500 * time.Millisecond)
	}

	return allJobs, nil
}

// searchPage performs a single page search using the correct API format
func (c *Client) searchPage(filters SearchFilters, page int) ([]Job, int, error) {
	reqBody := SearchRequest{
		Size: PageSize,
		Page: page - 1, // API uses 0-based indexing
		SearchState: SearchState{
			SearchQuery: filters.Query,
			SortBy:      "date",
		},
	}

	jsonBody, err := json.Marshal(reqBody)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to marshal request: %w", err)
	}

	log.Printf("Sending request to %s with body: %s", SearchURL, string(jsonBody))

	req, err := http.NewRequest("POST", SearchURL, bytes.NewBuffer(jsonBody))
	if err != nil {
		return nil, 0, fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", UserAgent)
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Origin", BaseURL)
	req.Header.Set("Referer", BaseURL+"/")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, 0, fmt.Errorf("API returned status %d: %s", resp.StatusCode, string(body))
	}

	var searchResp SearchResponse
	if err := json.Unmarshal(body, &searchResp); err != nil {
		return nil, 0, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	// Convert JobResult to Job
	var jobs []Job
	for _, result := range searchResp.Results {
		job := Job{
			ID:               result.ID,
			ApplyURL:         result.ApplyURL,
			Title:            result.JobInformation.Title,
			Description:      result.JobInformation.Description,
			Company:          result.ProcessedJobData.CompanyName,
			Location:         result.ProcessedJobData.FormattedWorkplaceLocation,
			Remote:           result.ProcessedJobData.WorkplaceType == "remote",
			DescriptionClean: result.JobInformation.Description,
		}

		// Parse salary if available
		if result.ProcessedJobData.IsCompensationTransparent &&
			result.ProcessedJobData.YearlyMinCompensation != nil &&
			result.ProcessedJobData.YearlyMaxCompensation != nil {
			job.Salary = fmt.Sprintf("$%.0f - $%.0f",
				*result.ProcessedJobData.YearlyMinCompensation,
				*result.ProcessedJobData.YearlyMaxCompensation)
		}

		// Parse posted date
		if result.ProcessedJobData.EstimatedPublishDateMillis != "" {
			// Try to parse as milliseconds timestamp
			var millis int64
			if _, err := fmt.Sscanf(result.ProcessedJobData.EstimatedPublishDateMillis, "%d", &millis); err == nil {
				job.PostedAt = time.UnixMilli(millis)
			}
		}

		jobs = append(jobs, job)
	}

	return jobs, searchResp.Total, nil
}
