import React, { useCallback, useEffect, useState } from 'react';

import type {
  IPromptConfig,
  PromptType,
} from '../../service/prompt-config/prompt-config.interface';
import {
  createPromptConfig,
  getAllPromptConfigs,
  updatePromptConfig,
} from '../../service/prompt-config/prompt-config.service';
import ErrorWrapper from '../alert/ErrorWrapper';
import { Spinner } from '../ui/Spinner';
import Banner from '../widgets/Banner';
import Button from '../widgets/Button';

type PromptConfigSettingsProps = {
  token: string | null;
};

type PromptFormState = {
  temperature: number;
  rules: string;
  tone: string;
  toneSelect: string;
};

const TONE_OPTIONS = [
  { value: '', label: 'Select a tone...', example: '' },
  {
    value: 'professional',
    label: 'Professional',
    example:
      '"This product features premium materials and verified craftsmanship."',
  },
  {
    value: 'friendly',
    label: 'Friendly',
    example:
      '"You\'re going to love this one! It\'s one of our best sellers for a reason."',
  },
  {
    value: 'casual',
    label: 'Casual',
    example: '"Check this out — super popular and totally worth it."',
  },
  {
    value: 'luxury',
    label: 'Luxury',
    example: '"An exquisite selection curated for the most discerning tastes."',
  },
  {
    value: 'playful',
    label: 'Playful',
    example:
      '"Ooh, great taste! This one practically jumped off the shelf for you."',
  },
  {
    value: 'custom',
    label: 'Custom',
    example: '',
  },
];

const defaultFormState: PromptFormState = {
  temperature: 0.7,
  rules: '',
  tone: '',
  toneSelect: '',
};

const TemperatureSlider = ({
  value,
  onChange,
}: {
  value: number;
  onChange: (val: number) => void;
}) => {
  const sliderValue = Math.round(value * 100);

  const getLabel = (v: number): string => {
    if (v <= 0.25) return 'Very Focused';
    if (v <= 0.5) return 'Balanced';
    if (v <= 0.75) return 'Creative';

    return 'Very Creative';
  };

  const getDescription = (v: number): string => {
    if (v <= 0.25)
      return 'Sticks closely to product facts. Best for technical or high-trust products.';
    if (v <= 0.5)
      return 'Balanced between accuracy and variety. Good for most stores.';
    if (v <= 0.75)
      return 'More varied and expressive responses. Great for lifestyle and fashion.';

    return 'Maximum creativity and variety. Best for discovery-oriented experiences.';
  };

  return (
    <div className="space-y-2">
      <div className="flex items-center justify-between">
        <p className="text-sm font-semibold text-gray-900">Temperature</p>
        <span className="inline-flex items-center gap-1.5 rounded-full bg-gray-100 px-2 py-0.5 text-[11px] font-semibold text-gray-600">
          {getLabel(value)}
          <span className="font-mono text-gray-400">{value.toFixed(1)}</span>
        </span>
      </div>

      <div className="space-y-1">
        <input
          type="range"
          min={0}
          max={100}
          step={5}
          value={sliderValue}
          onChange={e => onChange(Number(e.target.value) / 100)}
          className="w-full h-1.5 rounded-full appearance-none cursor-pointer bg-gray-200 accent-black
            [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4
            [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-black [&::-webkit-slider-thumb]:shadow-md
            [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-white
            [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:rounded-full
            [&::-moz-range-thumb]:bg-black [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-white"
        />
        <div className="flex justify-between text-[10px] text-gray-400">
          <span>Focused</span>
          <span>Creative</span>
        </div>
      </div>

      <p className="text-[11px] text-gray-500 leading-relaxed">
        {getDescription(value)}
      </p>
    </div>
  );
};

const ToneSelector = ({
  toneSelect,
  tone,
  onSelectChange,
  onToneChange,
}: {
  toneSelect: string;
  tone: string;
  onSelectChange: (val: string) => void;
  onToneChange: (val: string) => void;
}) => {
  const selectedTone = TONE_OPTIONS.find(t => t.value === toneSelect);

  return (
    <div className="space-y-2">
      <p className="text-sm font-semibold text-gray-900">Tone of Voice</p>
      <select
        value={toneSelect}
        onChange={e => onSelectChange(e.target.value)}
        className="w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs text-gray-700 focus:border-gray-400 focus:outline-none focus:ring-1 focus:ring-gray-200"
      >
        {TONE_OPTIONS.map(opt => (
          <option key={opt.value} value={opt.value}>
            {opt.label}
          </option>
        ))}
      </select>

      {toneSelect === 'custom' && (
        <div className="space-y-1 mt-2">
          <input
            type="text"
            value={tone}
            maxLength={50}
            onChange={e => onToneChange(e.target.value)}
            className="w-full rounded-md border border-gray-200 px-3 py-2 text-xs focus:outline-none focus:ring-1 focus:ring-gray-200"
            placeholder="e.g., Friendly but professional"
          />
          <p className="text-xs text-gray-500">
            Describe your custom tone (max 50 characters).
          </p>
        </div>
      )}

      {selectedTone?.example && toneSelect !== 'custom' && (
        <div className="rounded-lg bg-gray-50 border border-gray-100 px-3 py-2 mt-2">
          <p className="text-[11px] text-gray-400 mb-0.5">Example response:</p>
          <p className="text-xs italic text-gray-600">{selectedTone.example}</p>
        </div>
      )}
    </div>
  );
};

const RulesEditor = ({
  value,
  onChange,
  placeholder,
  hint,
  tip,
}: {
  value: string;
  onChange: (val: string) => void;
  placeholder?: string;
  hint?: string;
  tip?: string;
}) => (
  <div className="space-y-2">
    <p className="text-sm font-semibold text-gray-900">Rules & Instructions</p>
    <textarea
      value={value}
      onChange={e => onChange(e.target.value)}
      placeholder={placeholder}
      rows={5}
      className="w-full min-h-[100px] rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-xs text-gray-700 placeholder:text-gray-400 focus:border-gray-400 focus:outline-none focus:ring-1 focus:ring-gray-200 resize-y leading-relaxed"
    />
    {hint && <p className="text-xs text-gray-500">{hint}</p>}
    {tip && (
      <div className="rounded-lg bg-gray-50 border border-gray-100 px-3 py-2.5 mt-2">
        <p className="text-[10px] font-semibold text-gray-400 uppercase tracking-wider mb-1">
          Tip
        </p>
        <p className="text-[11px] text-gray-500 leading-relaxed">{tip}</p>
      </div>
    )}
  </div>
);

const ConfigSection = ({
  title,
  description,
  children,
  onSave,
  loading,
  buttonText = 'Save',
}: {
  title: string;
  description: string;
  children: React.ReactNode;
  onSave: () => void;
  loading: boolean;
  buttonText?: string;
}) => (
  <div className="rounded-xl border border-gray-200 bg-white overflow-hidden">
    <div className="bg-gray-50 border-b border-gray-200 px-4 py-3">
      <div className="flex items-center gap-2">
        <h3 className="text-sm font-semibold text-gray-900">{title}</h3>
        <div className="group relative">
          <div className="flex items-center justify-center w-4 h-4 rounded-full bg-gray-200 text-gray-500 cursor-help text-[10px]">
            i
          </div>
          <div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 hidden group-hover:block z-50">
            <div className="bg-gray-900 text-white text-[11px] rounded-lg px-3 py-2 max-w-[220px] shadow-lg leading-relaxed">
              {description}
              <div className="absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-gray-900" />
            </div>
          </div>
        </div>
      </div>
      <p className="text-xs text-gray-500 mt-0.5">{description}</p>
    </div>

    <div className="p-4 space-y-5">{children}</div>

    <div className="border-t border-gray-100 px-4 py-3 bg-gray-50/50 flex justify-end">
      <Button
        type="button"
        onClick={onSave}
        isLoading={loading}
        disabled={loading}
        className="w-full sm:w-auto rounded-md bg-gray-900 px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-50"
      >
        {buttonText}
      </Button>
    </div>
  </div>
);

const PromptConfigSettings = ({
  token,
}: PromptConfigSettingsProps): JSX.Element => {
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState<PromptType | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [successMessage, setSuccessMessage] = useState<string | null>(null);

  const [ragConfig, setRagConfig] = useState<IPromptConfig | null>(null);
  const [qaConfig, setQaConfig] = useState<IPromptConfig | null>(null);

  const [ragForm, setRagForm] = useState<PromptFormState>(defaultFormState);
  const [qaForm, setQaForm] = useState<PromptFormState>(defaultFormState);

  const fetchConfigs = useCallback(async () => {
    if (!token) {
      setError('Missing authentication token.');
      setLoading(false);

      return;
    }

    setLoading(true);
    setError(null);

    try {
      const response = await getAllPromptConfigs();

      if (response?.success && response?.prompt_configs) {
        const configs = response.prompt_configs;

        const rag = configs.find(c => c.type === 'RAG');
        const qa = configs.find(c => c.type === 'QA');

        if (rag) {
          setRagConfig(rag);
          const ragToneOption = TONE_OPTIONS.find(
            opt => opt.value === rag.tone
          );
          setRagForm({
            temperature: Number(rag.temperature),
            rules: rag.rules,
            tone: rag.tone,
            toneSelect: ragToneOption ? rag.tone : 'custom',
          });
        } else {
          setRagForm(defaultFormState);
        }

        if (qa) {
          setQaConfig(qa);
          const qaToneOption = TONE_OPTIONS.find(opt => opt.value === qa.tone);
          setQaForm({
            temperature: Number(qa.temperature),
            rules: qa.rules,
            tone: qa.tone,
            toneSelect: qaToneOption ? qa.tone : 'custom',
          });
        } else {
          setQaForm(defaultFormState);
        }
      } else {
        setError(response?.message || 'Failed to load prompt configurations.');
      }
    } catch (err) {
      console.error('Error fetching prompt configs:', err);
      setError('Failed to load prompt configurations.');
    } finally {
      setLoading(false);
    }
  }, [token]);

  useEffect(() => {
    fetchConfigs();
  }, [fetchConfigs]);

  const handleToneSelectChange = (
    value: string,
    setForm: React.Dispatch<React.SetStateAction<PromptFormState>>
  ) => {
    if (value === 'custom') {
      setForm(prev => ({ ...prev, toneSelect: 'custom', tone: '' }));
    } else {
      setForm(prev => ({ ...prev, toneSelect: value, tone: value }));
    }
  };

  const handleSave = async (type: PromptType) => {
    if (!token) {
      setError('Missing authentication token.');

      return;
    }

    const form = type === 'RAG' ? ragForm : qaForm;
    const existingConfig = type === 'RAG' ? ragConfig : qaConfig;

    const temperature = form.temperature;
    if (isNaN(temperature) || temperature < 0 || temperature > 2) {
      setError('Temperature must be a number between 0 and 2.');

      return;
    }

    if (!form.rules.trim()) {
      setError('Rules field is required.');

      return;
    }

    if (!form.tone.trim()) {
      setError('Tone field is required.');

      return;
    }

    if (form.tone.length > 50) {
      setError('Tone must be 50 characters or less.');

      return;
    }

    setSaving(type);
    setError(null);
    setSuccessMessage(null);

    try {
      const payload = {
        temperature,
        rules: form.rules,
        tone: form.tone,
      };

      let response;

      if (existingConfig) {
        response = await updatePromptConfig(existingConfig.id, payload);
      } else {
        response = await createPromptConfig({ ...payload, type });
      }

      if (response?.success) {
        setSuccessMessage(`${type} configuration saved successfully.`);
        if (response?.prompt_config) {
          if (type === 'RAG') {
            setRagConfig(response.prompt_config);
          } else {
            setQaConfig(response.prompt_config);
          }
        }
        await fetchConfigs();
      } else {
        setError(response?.message || 'Failed to save configuration.');
      }
    } catch (err) {
      console.error('Error saving prompt config:', err);
      setError('Failed to save configuration.');
    } finally {
      setSaving(null);
    }
  };

  if (loading) {
    return (
      <div className="flex items-center justify-center py-6">
        <Spinner className="h-6 w-6 text-gray-500" />
      </div>
    );
  }

  return (
    <div className="space-y-5">
      {error ? <ErrorWrapper error={error} /> : null}

      {successMessage ? (
        <Banner
          type="success"
          open={!!successMessage}
          setOpen={value => {
            if (!value) setSuccessMessage(null);
          }}
          className="pb-2"
        >
          {successMessage}
        </Banner>
      ) : null}

      <ConfigSection
        title="RAG Configuration"
        description="Controls how your AI responds when recommending products. Uses your product catalog to generate tailored recommendations."
        onSave={() => handleSave('RAG')}
        loading={saving === 'RAG'}
        buttonText={ragConfig ? 'Update Configuration' : 'Create Configuration'}
      >
        <div className="grid grid-cols-1 gap-4">
          <TemperatureSlider
            value={ragForm.temperature}
            onChange={v => setRagForm(prev => ({ ...prev, temperature: v }))}
          />
          <ToneSelector
            toneSelect={ragForm.toneSelect}
            tone={ragForm.tone}
            onSelectChange={v => handleToneSelectChange(v, setRagForm)}
            onToneChange={v => setRagForm(prev => ({ ...prev, tone: v }))}
          />
        </div>
        <RulesEditor
          value={ragForm.rules}
          onChange={v => setRagForm(prev => ({ ...prev, rules: v }))}
          placeholder="Always cite specs from catalog\nNever promise discounts\nKeep answers concise and on-brand"
          hint="One rule per line. Earlier rules have higher priority."
          tip="Write each rule on its own line. Be specific — 'Mention free shipping on orders over $50' works better than 'Talk about shipping.'"
        />
      </ConfigSection>

      <ConfigSection
        title="Q&A Configuration"
        description="Controls how your AI responds to general store questions — shipping, returns, sizing, policies, and anything not tied to a specific product."
        onSave={() => handleSave('QA')}
        loading={saving === 'QA'}
        buttonText={qaConfig ? 'Update Configuration' : 'Create Configuration'}
      >
        <div className="grid grid-cols-1 gap-4">
          <TemperatureSlider
            value={qaForm.temperature}
            onChange={v => setQaForm(prev => ({ ...prev, temperature: v }))}
          />
          <ToneSelector
            toneSelect={qaForm.toneSelect}
            tone={qaForm.tone}
            onSelectChange={v => handleToneSelectChange(v, setQaForm)}
            onToneChange={v => setQaForm(prev => ({ ...prev, tone: v }))}
          />
        </div>
        <RulesEditor
          value={qaForm.rules}
          onChange={v => setQaForm(prev => ({ ...prev, rules: v }))}
          placeholder="Always include a link to our returns policy\nMention 30-day guarantee on all products"
          hint="One rule per line. Earlier rules have higher priority."
          tip="Cover common topics: shipping timelines, return windows, sizing guides, and contact info. The clearer your rules, the better your AI responds."
        />
      </ConfigSection>
    </div>
  );
};

export default PromptConfigSettings;
