/**
 *
 * CapImageUrlUpload
 *
 * A modular component for uploading images from a URL.
 * Validates URL format, image type, and size before uploading to gallery.
 * Can be used in any form context (creatives, profiles, etc.)
 */

import React, { useState, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
import CapInput from '@capillarytech/cap-ui-library/CapInput';
import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
import CapError from '@capillarytech/cap-ui-library/CapError';
import messages from './messages';
import { DEFAULT_ALLOWED_CONTENT_TYPES, DEFAULT_MAX_SIZE, UPLOAD_STATUS } from './constants';
import { fetchImageFromUrl, uploadImageFromUrlHelper } from '../../utils/imageUrlUpload';
import './index.scss';

function CapImageUrlUpload(props) {
  const {
    intl,
    uploadAsset,
    imgSize = DEFAULT_MAX_SIZE,
    allowedContentTypes = DEFAULT_ALLOWED_CONTENT_TYPES,
    recommendedDimensions = [],
    sizeLabel = '',
    formatLabel = '',
    imageUrl = '',
    imageSrc = '', // Secure file path from parent after upload completes
    onUrlChange,
    onValidationStateChange, // Callback to notify parent of validation state
    onUploadStateChange, // Callback to notify parent of upload state
    isExternalUploading = false, // Upload state from parent (e.g., redux)
    className = '',
    placeholder,
    disabled = false,
    fileNamePrefix = 'image',
  } = props;

  const { formatMessage } = intl ?? {};
  const { CapHeadingSpan } = CapHeading;
  
  const [isValidating, setIsValidating] = useState(false);
  const [isInternalUploading, setIsInternalUploading] = useState(false);
  const [error, setError] = useState('');
  // State machine for upload lifecycle
  const [uploadStatus, setUploadStatus] = useState(UPLOAD_STATUS.IDLE);

  /**
   * Validate image URL
   * Checks URL format, fetches as Blob, validates Content-Type, file size, and image validity
   */
  const validateImageUrl = useCallback(async (url) => {
    const trimmedUrl = url?.trim() || '';
    
    if (!trimmedUrl) {
      return { isValid: false, error: '' };
    }

    setIsValidating(true);
    setError('');

    try {
      // Validate URL format
      let urlObj;
      try {
        urlObj = new URL(trimmedUrl);
        if (!['http:', 'https:'].includes(urlObj.protocol)) {
          setIsValidating(false);
          return {
            isValid: false,
            error: formatMessage(messages.imageUrlInvalid),
          };
        }
      } catch (urlError) {
        setIsValidating(false);
        return {
          isValid: false,
          error: formatMessage(messages.imageUrlInvalid),
        };
      }

      // Fetch image as Blob to check file size
      let response;
      try {
        response = await fetchImageFromUrl(trimmedUrl);
      } catch (fetchError) {
        setIsValidating(false);
        return {
          isValid: false,
          error: formatMessage(messages.imageLoadError),
        };
      }

      // Validate Content-Type
      const contentType = response.headers?.get('Content-Type') || '';
      const normalizedContentType = contentType.split(';')[0].toLowerCase().trim();
      
      if (!allowedContentTypes.includes(normalizedContentType)) {
        setIsValidating(false);
        return {
          isValid: false,
          error: formatMessage(messages.imageTypeInvalid),
        };
      }

      // Validate file size
      const blob = await response.blob();
      if (blob.size > imgSize) {
        setIsValidating(false);
        return {
          isValid: false,
          error: formatMessage(messages.imageSizeInvalid),
        };
      }

      // Verify image validity by loading in Image object
      return new Promise((resolve) => {
        const img = new Image();
        const objectUrl = URL.createObjectURL(blob);
        
        img.onload = () => {
          URL.revokeObjectURL(objectUrl);
          setIsValidating(false);
          resolve({
            isValid: true,
            error: '',
          });
        };
        
        img.onerror = () => {
          URL.revokeObjectURL(objectUrl);
          setIsValidating(false);
          resolve({
            isValid: false,
            error: formatMessage(messages.imageLoadError),
          });
        };
        
        img.src = objectUrl;
      });
    } catch (error) {
      setIsValidating(false);
      return {
        isValid: false,
        error: formatMessage(messages.imageLoadError),
      };
    }
  }, [formatMessage, allowedContentTypes, imgSize]);

  /**
   * Upload image from URL to media gallery
   * Fetches image, converts to File, uploads via uploadAsset
   */
  const uploadImageFromUrl = useCallback(async (url) => {
    setIsInternalUploading(true);
    setUploadStatus(UPLOAD_STATUS.UPLOADING);
    
    // Notify parent that upload is starting
    if (onUploadStateChange) {
      onUploadStateChange(true);
    }
    
    const result = await uploadImageFromUrlHelper(
      url,
      formatMessage,
      messages,
      uploadAsset,
      fileNamePrefix,
      imgSize,
      allowedContentTypes,
    );
    
    setIsInternalUploading(false);
    
    // Check if imageSrc is already available (handles React batching/timing issues)
    if (result.success && imageSrc && imageSrc !== '') {
      // Upload already complete - imageSrc was set before we entered waiting state
      setUploadStatus(UPLOAD_STATUS.IDLE);
      if (onUploadStateChange) {
        onUploadStateChange(false);
      }
    } else if (result.success) {
      // Transition to waiting state - we've triggered the upload, now wait for imageSrc
      setUploadStatus(UPLOAD_STATUS.WAITING);
    } else {
      // Upload failed
      setUploadStatus(UPLOAD_STATUS.IDLE);
      if (onUploadStateChange) {
        onUploadStateChange(false);
      }
    }
    
    return result;
  }, [formatMessage, uploadAsset, fileNamePrefix, imgSize, allowedContentTypes, onUploadStateChange, imageSrc]);

  /**
   * Handle image URL change
   * Validates URL and triggers upload on success
   */
  const handleImageUrlChange = useCallback(async (e) => {
    const url = e.target.value;
    const trimmedUrl = url?.trim() || '';
    
    // Reset upload status when URL changes
    setUploadStatus(UPLOAD_STATUS.IDLE);
    
    // Call parent's onUrlChange if provided
    if (onUrlChange) {
      onUrlChange(url);
    }
    
    if (trimmedUrl !== '') {
      // Notify parent that validation is starting
      if (onValidationStateChange) {
        onValidationStateChange(true);
      }
      
      const validation = await validateImageUrl(trimmedUrl);
      
      // Notify parent that validation is complete
      if (onValidationStateChange) {
        onValidationStateChange(false);
      }
      
      if (!validation.isValid) {
        setError(validation.error);
        if (onUploadStateChange) {
          onUploadStateChange(false);
        }
      } else {
        setError('');
        // Upload image from URL when validation succeeds
        const uploadResult = await uploadImageFromUrl(trimmedUrl);
        if (!uploadResult.success) {
          setError(uploadResult.error);
          if (onUploadStateChange) {
            onUploadStateChange(false);
          }
        }
      }
    } else {
      setError('');
      if (onValidationStateChange) {
        onValidationStateChange(false);
      }
      if (onUploadStateChange) {
        onUploadStateChange(false);
      }
    }
  }, [validateImageUrl, uploadImageFromUrl, onUrlChange, onValidationStateChange, onUploadStateChange]);

  /**
   * Monitor upload status and imageSrc to detect when upload is complete
   * Handles React batching and timing issues by checking state transitions
   */
  useEffect(() => {
    if ((uploadStatus === UPLOAD_STATUS.UPLOADING || uploadStatus === UPLOAD_STATUS.WAITING) && imageSrc && imageSrc !== '') {
      // Upload is complete - we have the secure file path
      setUploadStatus(UPLOAD_STATUS.IDLE);
      
      // Notify parent that upload is complete
      if (onUploadStateChange) {
        onUploadStateChange(false);
      }
    }
  }, [uploadStatus, imageSrc, onUploadStateChange]);

  // Determine if we should show "uploading" state
  // Show uploading when:
  // 1. Internal upload is in progress, OR
  // 2. External upload is in progress (from parent's redux), OR
  // 3. State machine indicates we're uploading or waiting for completion
  const isActuallyUploading = isInternalUploading || isExternalUploading || uploadStatus !== UPLOAD_STATUS.IDLE;

  return (
    <div className={`cap-image-url-upload ${className}`}>
      <CapInput
        className="image-url-input"
        placeholder={placeholder || formatMessage(messages.imageUrlPlaceholder)}
        value={imageUrl}
        onChange={handleImageUrlChange}
        size="default"
        errorMessage={
          error && (
            <CapError className="image-url-error">
              {error}
            </CapError>
          )
        }
        disabled={disabled || isValidating || isActuallyUploading}
      />
      
      {isValidating && (
        <CapLabel type="label2" className="validation-label">
          <FormattedMessage {...messages.validatingUrl} />
        </CapLabel>
      )}
      
      {isActuallyUploading && !isValidating && (
        <CapLabel type="label2" className="uploading-label">
          <FormattedMessage {...messages.uploadingImage} />
        </CapLabel>
      )}
      
      <div className="webpush-image-specs">
        {recommendedDimensions?.length > 0 && (
          <CapHeadingSpan type="label2" className="image-dimension">
            <FormattedMessage
              {...messages.recommendedDimensions}
              values={{
                dimensions: recommendedDimensions
                  .map((dim) => `${dim.width} x ${dim.height}px`)
                  .join(', '),
              }}
            />
          </CapHeadingSpan>
        )}
        
        {sizeLabel && (
          <CapHeadingSpan type="label2" className="image-size">
            {sizeLabel}
          </CapHeadingSpan>
        )}
        
        {formatLabel && (
          <CapHeadingSpan type="label2" className="image-format">
            {formatLabel}
          </CapHeadingSpan>
        )}
      </div>
    </div>
  );
}

CapImageUrlUpload.propTypes = {
  intl: intlShape.isRequired,
  uploadAsset: PropTypes.func.isRequired,
  imgSize: PropTypes.number,
  allowedContentTypes: PropTypes.arrayOf(PropTypes.string),
  recommendedDimensions: PropTypes.arrayOf(
    PropTypes.shape({
      width: PropTypes.number.isRequired,
      height: PropTypes.number.isRequired,
    })
  ),
  sizeLabel: PropTypes.string,
  formatLabel: PropTypes.string,
  imageUrl: PropTypes.string,
  imageSrc: PropTypes.string, // Secure file path from parent after upload completes
  onUrlChange: PropTypes.func,
  onValidationStateChange: PropTypes.func, // Callback(isValidating: bool)
  onUploadStateChange: PropTypes.func, // Callback(isUploading: bool)
  isExternalUploading: PropTypes.bool, // Upload state from parent (e.g., redux)
  className: PropTypes.string,
  placeholder: PropTypes.string,
  disabled: PropTypes.bool,
  fileNamePrefix: PropTypes.string,
};

export default injectIntl(CapImageUrlUpload);

