(function initValuationsAddModule(global) {
  const portfolioSelection = global.PortfolioSelection;
  if (!portfolioSelection) {
    throw new Error('PortfolioSelection must be loaded before PortfolioValuationsAdd.');
  }

  const {
    usePortfolios,
    PortfolioSelectField,
    getAuthorizedJsonHeaders,
    handleUnauthorizedApiResponse
  } = portfolioSelection;
  const shared = global.PortfolioSharedComponents || {};
  const BlockingScreenLoader = shared.BlockingScreenLoader;
  const SegmentedControl = shared.SegmentedControl;
  const appUtils = global.PortfolioApp || {};

  function formatAmount(value) {
    if (appUtils && typeof appUtils.formatAmount === 'function') {
      const formatted = appUtils.formatAmount(value);
      return formatted == null ? '0.00' : String(formatted);
    }
    const numeric = Number.parseFloat(value);
    if (!Number.isFinite(numeric)) return '0.00';
    return numeric.toFixed(2);
  }

  function parseCsvLine(line) {
    const cells = [];
    let current = '';
    let inQuotes = false;
    for (let i = 0; i < line.length; i += 1) {
      const ch = line[i];
      if (ch === '"') {
        if (inQuotes && line[i + 1] === '"') {
          current += '"'; i += 1; continue;
        }
        inQuotes = !inQuotes; continue;
      }
      if (ch === ',' && !inQuotes) { cells.push(current); current = ''; continue; }
      current += ch;
    }
    cells.push(current);
    return cells;
  }

  function toIsoDateString(rawValue) {
    const v = String(rawValue || '').trim().replace(/\//g, '-');
    if (!v) return null;
    const d = new Date(`${v}T00:00:00.000Z`);
    if (!Number.isFinite(d.getTime())) return null;
    return d.toISOString().slice(0, 10);
  }

  function ValuationsAddPanel(props) {
    const portfolios = Array.isArray(props.portfolios) ? props.portfolios : [];
    const navigate = typeof props.navigate === 'function' ? props.navigate : () => {};
    const resolvedGetAuthorizedJsonHeaders = typeof props.getAuthorizedJsonHeaders === 'function'
      ? props.getAuthorizedJsonHeaders
      : getAuthorizedJsonHeaders;

    const resolvedOnUnauthorized = typeof props.onUnauthorized === 'function'
      ? props.onUnauthorized
      : handleUnauthorizedApiResponse;
    const [mode, setMode] = React.useState('manual'); // 'manual' | 'file'
    const [selectedPortfolio, setSelectedPortfolio] = React.useState('');
    const [date, setDate] = React.useState('');
    const [dateError, setDateError] = React.useState('');
    const [portfolioError, setPortfolioError] = React.useState('');
    const [manualValue, setManualValue] = React.useState('');
    const [isBlocking, setIsBlocking] = React.useState(false);

    const [manualValueError, setManualValueError] = React.useState('');
    // File mode state
    const [fileName, setFileName] = React.useState('');
    const [fileText, setFileText] = React.useState('');
    const [parsedRows, setParsedRows] = React.useState([]);
    const [parseFailures, setParseFailures] = React.useState([]);
    const [fileStatus, setFileStatus] = React.useState(null);
    const [isParsing, setIsParsing] = React.useState(false);
    const [fileError, setFileError] = React.useState('');
    const [successMessage, setSuccessMessage] = React.useState(null);

    React.useEffect(() => {
      // reset validation when switching modes
      setFileStatus(null);
      setParseFailures([]);
      setParsedRows([]);
      setIsParsing(false);
    }, [mode]);

    function validateDateIsFriday(nextDate) {
      if (!nextDate) return 'Date is required.';
      const d = new Date(`${nextDate}T00:00:00.000Z`);
      if (!Number.isFinite(d.getTime())) return 'Invalid date.';
      const day = d.getUTCDay();
      if (day !== 5) return 'The date must be a Friday.';
      return '';
    }

    const handleDateChange = (ev) => {
      const next = ev.target.value;
      setDate(next);
      setDateError(validateDateIsFriday(next));
    };

    const handleFileSelected = (ev) => {
      const file = ev.target.files && ev.target.files[0] ? ev.target.files[0] : null;
      if (!file) return;
      if (!file.name || !/\.csv$/i.test(file.name)) {
        setFileStatus({ type: 'error', message: 'Only .csv files are supported.' });
        setFileError('Only .csv files are supported.');
        return;
      }

      const reader = new FileReader();
      setIsParsing(true);
      reader.onload = () => {
        const text = String(reader.result || '');
        setFileName(file.name);
        setFileText(text);
        parsePositionFile(text);
        setIsParsing(false);
        setFileError('');
      };
      reader.onerror = () => { setIsParsing(false); setFileStatus({ type: 'error', message: 'Could not read file.' }); setFileError('Could not read file.'); };
      reader.readAsText(file);
    };

    function parsePositionFile(text) {
      const lines = String(text || '').split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
      if (lines.length === 0) {
        setParsedRows([]); setParseFailures([]); setFileStatus({ type: 'error', message: 'Empty file.' }); return;
      }

      const headers = parseCsvLine(lines[0]).map((c) => String(c || '').trim().toLowerCase());
      const expected = ['product','symbol/isin','amount','closing','local value','','value in eur'];
      const match = expected.every((val, idx) => (val === '' ? true : headers[idx] === val));
      if (!match) {
        setParsedRows([]); setParseFailures(lines.slice(1)); setFileStatus({ type: 'error', message: 'Invalid Degiro Position heading row.' }); return;
      }

      const valid = [];
      const failures = [];

      for (const raw of lines.slice(1)) {
        const cells = parseCsvLine(raw).map((c) => String(c || '').trim());
        if (cells.length < 7) { failures.push(raw); continue; }
        const product = cells[0];
        const symbol = cells[1];
        const amount = Number.parseFloat(cells[2].replace(/,/g, ''));
        const closing = cells[3];
        // Use mapping matching Degiro sample: currency is column 5 (cells[4]), localValue is column 6 (cells[5]), Value in EUR is column 7 (cells[6]).
        const currencyRaw = String(cells[4] || '').trim();
        const currency = currencyRaw ? currencyRaw.toUpperCase() : null;
        const localValue = Number.parseFloat((cells[5] || '').replace(/,/g, ''));
        const valueEur = Number.parseFloat((cells[6] || '').replace(/,/g, ''));

        // Enforce presence/format: currency must be a 3-letter code and valueEur must be numeric
        if (!currency || !/^[A-Z]{3}$/.test(currency) || !Number.isFinite(valueEur)) {
          failures.push(raw);
          continue;
        }
        const isCash = typeof product === 'string' && /^\s*CASH/i.test(product);

        // Cash rows: Product begins with CASH. These rows should be accepted if Value in EUR is present.
        if (isCash) {
          if (!Number.isFinite(valueEur)) { failures.push(raw); continue; }
          // For cash, ISIN, Quantity and Price should be null per spec
          valid.push({ product, symbol: null, amount: null, closing: null, localValue, valueEur, currency, raw });
          continue;
        }

        // Non-cash rows must have a symbol and a numeric amount and Value in EUR
        if (!product || !symbol || !Number.isFinite(amount) || !Number.isFinite(valueEur)) {
          failures.push(raw); continue;
        }

        valid.push({ product, symbol, amount, closing, localValue, valueEur, currency, raw });
      }

      setParsedRows(valid);
      setParseFailures(failures);
      if (valid.length === 0) {
        setFileStatus({ type: 'error', message: 'No valid positions found.' });
      } else if (failures.length > 0) {
        setFileStatus({ type: 'error', message: 'File contains invalid rows.' });
      } else {
        setFileStatus({ type: 'success', message: `${valid.length} valid positions parsed.` });
      }
    }

    const canAddManual = selectedPortfolio && date && !dateError && manualValue !== '';
    // Show Add button for file mode once preview parsed successfully, even if portfolio/date not set
    const canAddFromFile = parsedRows.length > 1 && parseFailures.length === 0;

    const handleAddValuation = (ev) => {
      ev && ev.preventDefault && ev.preventDefault();

      // validate on submit
      const nextDateError = validateDateIsFriday(date);
      setDateError(nextDateError);
      if (!selectedPortfolio) {
        setPortfolioError('Portfolio is required.');
      }

      const validManual = selectedPortfolio && !nextDateError && manualValue !== '';
      const validFile = selectedPortfolio && !nextDateError && parsedRows.length > 1 && parseFailures.length === 0;

      if (!(validManual || validFile)) {
        const missing = [];
        if (!selectedPortfolio) missing.push('Portfolio');
        if (nextDateError) missing.push('Date');
        if (mode === 'manual' && (manualValue === '' || manualValue == null)) missing.push('Value');
        if (mode === 'file' && (!Array.isArray(parsedRows) || parsedRows.length <= 1 || parseFailures.length > 0)) missing.push('File');
        // set field-level errors
        if (!selectedPortfolio) setPortfolioError('Portfolio is required.');
        if (nextDateError) setDateError(nextDateError);
        if (missing.indexOf('Value') !== -1) setManualValueError('Value is required.');
        if (missing.indexOf('File') !== -1) setFileError('File is required or contains invalid rows.');
        setFileStatus({ type: 'error', message: `Please provide: ${missing.join(', ')}.` });
        return;
      }

      setIsBlocking(true);

      (async () => {
        try {
          const rows = [];
          if (validManual) {
            rows.push({ date, values: { [selectedPortfolio]: manualValue } });
          } else if (validFile) {
            // build a single row with positions array
            const vals = { [selectedPortfolio]: parsedRows.reduce((acc, r) => acc + (Number.isFinite(r.valueEur) ? Number(r.valueEur) : 0), 0) };
            const positions = parsedRows.map((r) => ({
              ISIN: r.symbol,
              Quantity: Number.isFinite(r.amount) ? Math.round(r.amount) : null,
              Price: Number.isFinite(Number(r.closing)) ? Number(r.closing) : null,
              Currency: r.currency,
              Value: Number.isFinite(r.localValue) ? r.localValue : 0,
              ValueConverted: Number.isFinite(r.valueEur) ? r.valueEur : 0
            }));

            rows.push({ date, values: vals, positions });
          }

          const payload = {
            portfolioIds: [selectedPortfolio],
            bypassDuplicateCheck: true,
            rows
          };


          const response = await fetch('/api/valuations', {
            method: 'POST',
            headers: Object.assign({ 'Content-Type': 'application/json' }, resolvedGetAuthorizedJsonHeaders()),
            body: JSON.stringify(payload)
          });

          if (response.status === 401 || response.status === 403) {
            if (typeof resolvedOnUnauthorized === 'function') {
              resolvedOnUnauthorized(response);
            } else {
              window.sessionStorage.removeItem('portfolio-auth-token');
              window.location.assign('/auth/login');
            }
            setIsBlocking(false);
            return;
          }

          if (!response.ok) {
            const json = await response.json().catch(() => ({}));
            setIsBlocking(false);
            setFileStatus({ type: 'error', message: json && json.code ? `Server error ${json.code}` : 'Server error' });
            return;
          }

          setIsBlocking(false);
          // Show success message in-panel instead of redirecting
          const msg = 'Valuation saved successfully.';
          setSuccessMessage(msg);
          setFileStatus({ type: 'success', message: msg });
        } catch (err) {
          setIsBlocking(false);
          setFileStatus({ type: 'error', message: 'Failed to save valuation.' });
        }
      })();
    };

    return (
      <section className="panel-card valuations-panel-card">
        <BlockingScreenLoader isActive={isBlocking} title="Adding Valuation" message="Please wait while the valuation is being processed." />

        <div className="valuations-header-row">
          <h3 style={{ marginTop: 0, marginBottom: 0 }}>Add Valuation</h3>
          <button type="button" className="secondary-btn" onClick={() => navigate('/valuations')}>Back to Valuations</button>
        </div>

        <p className="muted upload-note">Manually enter an end of week valuation or select a Degiro Positions file.</p>

        <div className="upload-warning-box">
          <ul>
            <li>Adding a valuation overrides any existing valuation for the same date.</li>
            <li>Only end of week valuations are supported. The date selected must be a Friday.</li>
            <li>Only .csv format is supported for Degiro Positions files.</li>
          </ul>
        </div>

        <div className="valuations-filters-row">
          <div className={`field ${portfolioError ? 'invalid' : ''}`}>
            <PortfolioSelectField
              portfolios={portfolios}
              value={selectedPortfolio}
              onChange={(v) => { setSelectedPortfolio(v); setPortfolioError(''); }}
              label="Portfolio"
              includeAll={false}
              placeholder="Select portfolio"
            />
            {portfolioError ? <span className="error-message">{portfolioError}</span> : null}
          </div>

          <label className={`field ${dateError ? 'invalid' : ''}`}>
            <span>Date</span>
            <input type="date" value={date} onChange={handleDateChange} />
            {dateError ? <span className="error-message">{dateError}</span> : null}
          </label>
          <div className="valuations-mode-field">
            <label className="field">
              <span>&nbsp;</span>
              {typeof SegmentedControl === 'function' ? (
                <SegmentedControl
                  options={[{ label: 'Manual entry', value: 'manual' }, { label: 'Degiro Positions File', value: 'file' }]}
                  value={mode}
                  onChange={(v) => setMode(v)}
                  ariaLabel="Valuation entry mode"
                  className="valuations-range-segmented performance-range-segmented"
                />
              ) : (
                <div className="shared-segmented-control" role="group" aria-label="Valuation entry mode">
                  <button type="button" className={mode === 'manual' ? 'primary-btn' : 'secondary-btn'} onClick={() => setMode('manual')}>Manual entry</button>
                  <button type="button" className={mode === 'file' ? 'primary-btn' : 'secondary-btn'} onClick={() => setMode('file')}>Degiro Positions File</button>
                </div>
              )}
            </label>
          </div>
        </div>
        
        

        {mode === 'manual' ? (
          <form onSubmit={handleAddValuation}>
            <p className="muted upload-note">Enter the total end of day Friday value for the portfolio including any cash positions.</p>
                <label className={`field ${manualValueError ? 'invalid' : ''}`}>
                  <span>Value</span>
                  <input type="number" step="0.01" value={manualValue} onChange={(e) => { setManualValue(e.target.value); setManualValueError(''); setFileStatus(null); }} placeholder="Enter total end of day Friday value" />
                  {manualValueError ? <span className="error-message">{manualValueError}</span> : null}
                </label>

            {fileStatus ? (
              <div className={`status-message ${fileStatus.type}`} role="status"><span>{fileStatus.message}</span></div>
            ) : null}

            {!successMessage ? (
              <div className="hero-actions upload-hero-actions">
                <button type="submit" className="primary-btn" disabled={isBlocking}>Add Valuation</button>
              </div>
            ) : (
              <div className="status-message success" role="status"><span>{successMessage}</span></div>
            )}
          </form>
        ) : (
          <section>
              <label className={`field ${fileError ? 'invalid' : ''}`}>
                <span>Degiro Positions File</span>
                <input type="file" accept=".csv,text/csv" onChange={(e) => { setFileError(''); setFileStatus(null); handleFileSelected(e); }} />
                {fileError ? <span className="error-message">{fileError}</span> : null}
              </label>

            <div className="upload-table-wrap">
              <table className="transactions-table guidance-table">
                <thead>
                  <tr>
                    <th>Degiro File Type</th>
                    <th>Description</th>
                    <th>Location (in Degiro)</th>
                  </tr>
                </thead>
                <tbody>
                  <tr>
                    <td>Degiro Positions File</td>
                    <td>
                        <ul>
                            <li>Contains the current positions in your Degiro account (including Cash).</li>
                            <li>Is does not include date information so it needs to be downloaded at the correct time.</li>
                            <li><b>Recommendation</b> to get the positions for that week:
                                <ul>
                                    <li>Download on a Fri (after market close) or Sat/Sun</li>
                                </ul>
                               
                            </li>
                        </ul>
                    </td>
                    <td>
                        <ol>
                            <li>Click Positions</li>
                            <li>Ensure Open Open Positions is selected (selected by default)</li>
                            <li>Click Download and choose .csv</li>
                            <li>The downloaded file will be titled: <i>Potfolio.csv</i></li>
                        </ol>
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>

            

            {parsedRows.length > 0 ? (
              <div>
                <div className="upload-table-wrap">
                  <table className="transactions-table">
                    <thead>
                      <tr>
                        <th>ISIN</th>
                        <th className="txn-cell-amount">Qty</th>
                        <th className="txn-cell-amount">Price</th>
                        <th>Currency</th>
                        <th className="txn-cell-amount">Value</th>
                        <th className="txn-cell-amount">Value</th>
                      </tr>
                    </thead>
                    <tbody>
                      {(() => {
                        const selectedPortfolioObj = portfolios.find((p) => String((p.Id != null ? p.Id : p.id)) === String(selectedPortfolio));
                        const primaryCurrency = selectedPortfolioObj && (selectedPortfolioObj.BaseCurrency || selectedPortfolioObj.baseCurrency)
                          ? String(selectedPortfolioObj.BaseCurrency || selectedPortfolioObj.baseCurrency).toUpperCase()
                          : null;
                        const primarySymbol = (shared && typeof shared.getCurrencySymbol === 'function')
                          ? shared.getCurrencySymbol(primaryCurrency)
                          : '';

                        return parsedRows.map((row, idx) => {
                        const isin = (row.symbol == null && row.product && (/^\s*CASH/i).test(row.product)) ? 'Cash' : (row.symbol || '');
                        const quantity = Number.isFinite(row.amount) ? Math.round(row.amount) : '-';
                        const closingNum = Number.parseFloat(row.closing);
                        const price = Number.isFinite(closingNum) ? formatAmount(closingNum) : (row.closing || '-');
                        const currencyCode = row.currency || '';
                        const localValue = Number.isFinite(row.localValue) ? row.localValue : null;
                        const valueConverted = Number.isFinite(row.valueEur) ? row.valueEur : null;

                        const localSymbol = (shared && typeof shared.getCurrencySymbol === 'function')
                          ? shared.getCurrencySymbol(currencyCode)
                          : '';

                        return (
                          <tr key={`${isin}-${idx}`}>
                            <td>{isin}</td>
                            <td className="txn-cell-amount">{quantity}</td>
                            <td className="txn-cell-amount">{price !== '-' ? `${localSymbol}${price}` : '-'}</td>
                            <td>{currencyCode}</td>
                            <td className="txn-cell-amount">{localValue != null ? `${localSymbol} ${formatAmount(localValue)}` : '-'}</td>
                            <td className="txn-cell-amount">{valueConverted != null ? `${primarySymbol} ${formatAmount(valueConverted)}` : '-'}</td>
                          </tr>
                        );
                        });
                      })()}

                      {/* Total row: show total of ValueConverted in last column, 'Total' in ISIN column */}
                      <tr className="upload-total-row">
                        <td><strong>Total</strong></td>
                        <td colSpan={4} />
                          <td className="txn-cell-amount"><strong>{(() => {
                          const total = parsedRows.reduce((acc, r) => acc + (Number.isFinite(r.valueEur) ? r.valueEur : 0), 0);
                          // reuse primarySymbol computed above
                          const selectedPortfolioObj = portfolios.find((p) => String((p.Id != null ? p.Id : p.id)) === String(selectedPortfolio));
                          const primaryCurrency = selectedPortfolioObj && (selectedPortfolioObj.BaseCurrency || selectedPortfolioObj.baseCurrency)
                            ? String(selectedPortfolioObj.BaseCurrency || selectedPortfolioObj.baseCurrency).toUpperCase()
                            : null;
                          const primarySymbolForTotal = (shared && typeof shared.getCurrencySymbol === 'function')
                            ? shared.getCurrencySymbol(primaryCurrency)
                            : '';
                          return `${primarySymbolForTotal} ${formatAmount(total)}`;
                        })()}</strong></td>
                      </tr>
                    </tbody>
                  </table>
                </div>
              </div>
            ) : null}

            {parseFailures.length > 0 ? (
              <div className="upload-parse-failures">
                <strong>Rows that could not be parsed</strong>
                <pre className="upload-parse-failures-raw">{parseFailures.join('\n')}</pre>
              </div>
            ) : null}

            {fileStatus ? (
              <div className={`status-message ${fileStatus.type}`} role="status"><span>{fileStatus.message}</span></div>
            ) : null}

            {!isParsing && !successMessage ? (
              <div className="hero-actions upload-hero-actions">
                <button type="button" className="primary-btn" onClick={handleAddValuation}>Add Valuation</button>
              </div>
            ) : null}
          </section>
        )}
      </section>
    );
  }

  global.PortfolioValuationsAdd = { ValuationsAddPanel };
})(window);
