/**
 * 会員管理ページ
 *
 * 一覧・検索・作成・編集・CSVインポート。
 */
import { useState, useEffect, useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import {
	Button,
	TextControl,
	SelectControl,
	Modal,
	Notice,
	FormFileUpload,
	CheckboxControl,
} from '@wordpress/components';
import DataTable from '../components/DataTable';
import { useMemberApi } from '../hooks/useApi';
import useCan from '../hooks/useCan';
import GuideSection from '../components/GuideSection';
import { decorateMemberTypeOptions } from '../../common/member-type-label';

const MEMBER_TYPES_BASE = [
	{ label: __( 'Regular Member', 'conferencemanager' ), value: 'regular' },
	{ label: __( 'Student Member', 'conferencemanager' ), value: 'student' },
	{ label: __( 'Associate Member', 'conferencemanager' ), value: 'associate' },
	{ label: __( 'Honorary Member', 'conferencemanager' ), value: 'honorary' },
	{ label: __( 'Non-member', 'conferencemanager' ), value: 'general' },
];

// 🔴 表示名の装飾はここ 1 箇所だけ。**元の定義（上）は 1 文字も変えない。**
// 学会の略称（初期設定）が入っていれば標準 5 区分にだけ前置する。
// ⚠️ サーバから来る値（`fee_type_label` など）は**サーバ側でだけ**装飾している。
//    ここで二重に通さないこと。
const MEMBER_TYPES = decorateMemberTypeOptions( MEMBER_TYPES_BASE );

const COLUMNS = [
	{ key: 'id', label: 'ID' },
	{ key: 'member_no', label: __( 'Member No.', 'conferencemanager' ) },
	{
		key: 'name',
		label: __( 'Name', 'conferencemanager' ),
		render: ( _, row ) => `${ row.name_sei } ${ row.name_mei }`,
	},
	{
		key: 'member_type',
		label: __( 'Type', 'conferencemanager' ),
		render: ( val ) => {
			const type = MEMBER_TYPES.find( ( t ) => t.value === val );
			return type ? type.label : val;
		},
	},
	{ key: 'email', label: __( 'Email', 'conferencemanager' ) },
	{ key: 'org_name', label: __( 'Organization', 'conferencemanager' ) },
	{
		key: 'approval_status',
		label: __( 'Approved', 'conferencemanager' ),
		render: ( val ) => {
			const labels = {
				auto_approved: __( 'Auto-approve', 'conferencemanager' ),
				approved: __( 'Approved', 'conferencemanager' ),
				pending: __( 'Pending Approval', 'conferencemanager' ),
				rejected: __( 'Rejected', 'conferencemanager' ),
			};
			if ( ! val ) return '';

			// 「自動承認」と「承認済」は**履歴の違い**（承認を経ていないか、経たか）で、
			// 自動承認が後から承認済に変わることはない（approve() は pending だけ受け付ける）。
			// 取り違えられやすいので title に意味を書いておく。
			const titles = {
				auto_approved: __( 'Registered without needing approval, because administrator approval is off.', 'conferencemanager' ),
				approved: __( 'An administrator approved this registration.', 'conferencemanager' ),
				pending: __( 'Waiting for an administrator to approve or reject.', 'conferencemanager' ),
				rejected: __( 'An administrator rejected this registration.', 'conferencemanager' ),
			};

			// 🔴 リンク色（#2271b1）で状態を出さない。押せるものに見えて実際に押される。
			//    色は admin.css の .conf-manager-status-badge--approval_* に持たせてある。
			const modifiers = {
				auto_approved: 'approval_auto',
				approved: 'approval_done',
				pending: 'approval_wait',
				rejected: 'approval_rejected',
			};
			const modifier = modifiers[ val ];

			return (
				<span
					className={ modifier
						? `conf-manager-status-badge conf-manager-status-badge--${ modifier }`
						: 'conf-manager-status-badge' }
					title={ titles[ val ] || '' }
				>
					{ labels[ val ] || val }
				</span>
			);
		},
	},
];

/**
 * 一覧の上に「いまの承認の運用」を 1 行出す。
 *
 * 🔴 決め打ちで「現在は自動承認です」と書かないこと。管理者承認をオンにしている
 *    サイトでは嘘の案内になる。実際の設定（conf_manager_require_approval）で出し分ける。
 *
 * 🔴 設定値は REST（/admin/system-status）から取らない。あの経路は
 *    conf_manager_manage_settings を要求するが、**「学会編集者」は会員を編集できるのに
 *    設定権限を持たない**ので、会員管理は開けるのに案内だけ 403 になる。
 *    admin/class-admin-menu.php の wp_localize_script で渡している。
 *
 * 📌 wp_localize_script は真偽値を "1" / "" にして渡すので、`!!` で読む（"" は falsy）。
 * 📌 設定画面へのリンクは manage_settings を持つ人にだけ出す。持たない人に見せると
 *    押した先で弾かれる（App.jsx の ROUTE_CAPS）。
 */
function ApprovalPolicyNote() {
	const requireApproval = !! window.confManagerSettings?.require_approval;
	const canManageSettings = useCan( 'conf_manager_manage_settings' );

	return (
		<p className="conf-manager-approval-note">
			{ requireApproval
				? __( 'Administrator approval is on: members who register themselves stay in "Pending Approval" until you approve them here.', 'conferencemanager' )
				: __( 'Administrator approval is off: members who register themselves are approved automatically.', 'conferencemanager' ) }
			{ /* 承認が既に必須のときは設定へ誘導しない（変えるべきことが無い）。 */ }
			{ ! requireApproval && canManageSettings && (
				<>
					{ ' ' }
					<a href="#/setup">{ __( 'Change this in Setup', 'conferencemanager' ) }</a>
				</>
			) }
		</p>
	);
}

const EMPTY_FORM = {
	member_no: '',
	is_member: true,
	member_type: 'regular',
	name_sei: '',
	name_mei: '',
	name_sei_kana: '',
	name_mei_kana: '',
	email: '',
	phone: '',
	org_name: '',
	org_dept: '',
	org_address: '',
};

export default function Members() {
	const api = useMemberApi();

	const [ members, setMembers ] = useState( [] );
	const [ total, setTotal ] = useState( 0 );
	const [ page, setPage ] = useState( 1 );
	const [ keyword, setKeyword ] = useState( '' );
	const [ typeFilter, setTypeFilter ] = useState( '' );
	const [ memberFilter, setMemberFilter ] = useState( '' );
	const [ approvalFilter, setApprovalFilter ] = useState( '' );

	const [ isModalOpen, setModalOpen ] = useState( false );
	const [ editingId, setEditingId ] = useState( null );
	const [ form, setForm ] = useState( { ...EMPTY_FORM } );
	const [ notice, setNotice ] = useState( null );

	const [ isImportOpen, setImportOpen ] = useState( false );
	const [ importFile, setImportFile ] = useState( null );
	const [ importPreview, setImportPreview ] = useState( null );
	const [ importEncoding, setImportEncoding ] = useState( 'UTF-8' );
	const [ importUpdateExisting, setImportUpdateExisting ] = useState( false );
	const [ importLoading, setImportLoading ] = useState( false );

	const fetchMembers = useCallback( async () => {
		try {
			const params = { page, per_page: 20 };
			if ( keyword ) {
				params.keyword = keyword;
			}
			if ( typeFilter ) {
				params.member_type = typeFilter;
			}
			if ( memberFilter !== '' ) {
				params.is_member = memberFilter;
			}
			if ( approvalFilter ) {
				params.approval_status = approvalFilter;
				if ( approvalFilter === 'pending' ) {
					params.status = 'inactive';
				}
			}
			const result = await api.getMembers( params );
			setMembers( result.items );
			setTotal( result.total );
		} catch ( err ) {
			// error handled by hook
		}
	}, [ page, keyword, typeFilter, memberFilter, approvalFilter ] );

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

	const handleSearch = ( e ) => {
		e.preventDefault();
		setPage( 1 );
		fetchMembers();
	};

	const openCreate = () => {
		setEditingId( null );
		setForm( { ...EMPTY_FORM } );
		setModalOpen( true );
	};

	const openEdit = ( member ) => {
		setEditingId( member.id );
		setForm( {
			member_no: member.member_no || '',
			is_member: !! member.is_member,
			member_type: member.member_type,
			name_sei: member.name_sei,
			name_mei: member.name_mei,
			name_sei_kana: member.name_sei_kana || '',
			name_mei_kana: member.name_mei_kana || '',
			email: member.email,
			phone: member.phone || '',
			org_name: member.org_name || '',
			org_dept: member.org_dept || '',
			org_address: member.org_address || '',
		} );
		setModalOpen( true );
	};

	const handleSubmit = async () => {
		try {
			if ( editingId ) {
				await api.updateMember( editingId, form );
				setNotice( { status: 'success', message: __( 'Member updated.', 'conferencemanager' ) } );
			} else {
				await api.createMember( form );
				setNotice( { status: 'success', message: __( 'Member created.', 'conferencemanager' ) } );
			}
			setModalOpen( false );
			fetchMembers();
		} catch ( err ) {
			setNotice( { status: 'error', message: err.message } );
		}
	};

	const handleApprove = async ( id ) => {
		try {
			await api.request( { path: `/conf-manager/v1/members/${ id }/approve`, method: 'POST' } );
			setNotice( { status: 'success', message: __( 'Member approved.', 'conferencemanager' ) } );
			fetchMembers();
		} catch ( err ) {
			setNotice( { status: 'error', message: err.message } );
		}
	};

	const handleReject = async ( id ) => {
		if ( ! window.confirm( __( 'Reject this member registration?', 'conferencemanager' ) ) ) return;
		try {
			await api.request( { path: `/conf-manager/v1/members/${ id }/reject`, method: 'POST' } );
			setNotice( { status: 'success', message: __( 'Member registration rejected.', 'conferencemanager' ) } );
			fetchMembers();
		} catch ( err ) {
			setNotice( { status: 'error', message: err.message } );
		}
	};

	const handleDelete = async ( id ) => {
		if ( ! window.confirm( __( 'Delete this member?', 'conferencemanager' ) ) ) {
			return;
		}
		try {
			await api.deleteMember( id );
			setNotice( { status: 'success', message: __( 'Member deleted.', 'conferencemanager' ) } );
			fetchMembers();
		} catch ( err ) {
			setNotice( { status: 'error', message: err.message } );
		}
	};

	const handleImportFileSelect = ( event ) => {
		const file = event.target.files[ 0 ];
		if ( ! file ) return;
		setImportFile( file );

		// クライアント側でプレビュー読込
		const reader = new FileReader();
		reader.onload = ( e ) => {
			const text = e.target.result;
			const lines = text.split( /\r?\n/ ).filter( ( l ) => l.trim() );
			const header = lines[ 0 ] ? lines[ 0 ].split( ',' ).map( ( c ) => c.trim().replace( /^"|"$/g, '' ) ) : [];
			const dataRows = lines.slice( 1 );
			const previewRows = dataRows.slice( 0, 5 ).map( ( row ) => row.split( ',' ).map( ( c ) => c.trim().replace( /^"|"$/g, '' ) ) );
			setImportPreview( { header, totalRows: dataRows.length, previewRows } );
		};
		reader.readAsText( file, importEncoding );
	};

	const handleImportExecute = async () => {
		if ( ! importFile ) return;
		setImportLoading( true );

		const formData = new FormData();
		formData.append( 'file', importFile );
		formData.append( 'encoding', importEncoding );
		formData.append( 'update_existing', importUpdateExisting ? '1' : '0' );

		try {
			const result = await api.request( {
				path: '/conf-manager/v1/members/import',
				method: 'POST',
				body: formData,
				headers: {},
			} );
			const parts = [];
			if ( result.imported ) parts.push( `${ result.imported }${ __( 'added', 'conferencemanager' ) }` );
			if ( result.updated ) parts.push( `${ result.updated }${ __( ' to update', 'conferencemanager' ) }` );
			if ( result.skipped ) parts.push( `${ result.skipped }${ __( 'skipped', 'conferencemanager' ) }` );
			if ( result.errors?.length ) parts.push( `${ result.errors.length }${ __( ' error(s)', 'conferencemanager' ) }` );
			setNotice( { status: result.errors?.length ? 'warning' : 'success', message: `${ __( 'Import complete:', 'conferencemanager' ) } ${ parts.join( ', ' ) }` } );
			setImportOpen( false );
			setImportFile( null );
			setImportPreview( null );
			fetchMembers();
		} catch ( err ) {
			setNotice( { status: 'error', message: err.message } );
		} finally {
			setImportLoading( false );
		}
	};

	const closeImportModal = () => {
		setImportOpen( false );
		setImportFile( null );
		setImportPreview( null );
	};

	const updateField = ( field ) => ( value ) => {
		setForm( ( prev ) => ( { ...prev, [ field ]: value } ) );
	};

	return (
		<div>
			<div className="conf-manager-page-header">
				<h2>{ __( 'Members', 'conferencemanager' ) }</h2>
			</div>

			<GuideSection
				hero={ {
					title: __( 'Centralized Member Management', 'conferencemanager' ),
					description: __( 'Securely manage regular, student, associate, and honorary members — integrated with event participation and payments.', 'conferencemanager' ),
				} }
				cards={ [
					{
						icon: <svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" /><circle cx="9" cy="7" r="4" /><path d="M23 21v-2a4 4 0 00-3-3.87" /><path d="M16 3.13a4 4 0 010 7.75" /></svg>,
						title: __( 'Member Registration', 'conferencemanager' ),
						description: __( 'Supports manual registration and bulk CSV import.', 'conferencemanager' ),
						tips: [
							__( 'Member numbers can be auto-generated or manually set', 'conferencemanager' ),
							__( 'Email addresses are encrypted for secure storage', 'conferencemanager' ),
							__( 'Search and filter by organization and department', 'conferencemanager' ),
						],
					},
					{
						icon: <svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" /><polyline points="17 8 12 3 7 8" /><line x1="12" y1="3" x2="12" y2="15" /></svg>,
						title: __( 'Bulk CSV Import', 'conferencemanager' ),
						description: __( 'Bulk import existing member lists from CSV files.', 'conferencemanager' ),
						tips: [
							__( 'Upload a CSV with header row', 'conferencemanager' ),
							__( 'Duplicate detection is automatic based on email address', 'conferencemanager' ),
							__( 'Preview import results before confirming', 'conferencemanager' ),
						],
					},
					{
						icon: <svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="3" y="3" width="18" height="18" rx="2" ry="2" /><line x1="3" y1="9" x2="21" y2="9" /><line x1="9" y1="21" x2="9" y2="9" /></svg>,
						title: __( 'Using Member Types', 'conferencemanager' ),
						description: __( 'Automate fee calculations based on member type for efficient pricing management.', 'conferencemanager' ),
						tips: [
							__( 'Set fees by member type and participation type per event', 'conferencemanager' ),
							__( 'Supports pricing for non-members (general) as well', 'conferencemanager' ),
							__( 'Associate members integrate with sponsor management', 'conferencemanager' ),
						],
					},
				] }
			/>

			{ notice && (
				<Notice
					status={ notice.status }
					onDismiss={ () => setNotice( null ) }
				>
					{ notice.message }
				</Notice>
			) }

			<form onSubmit={ handleSearch } className="conf-manager-search">
				<TextControl
					placeholder={ __( 'Search by name, kana, organization, or member no.', 'conferencemanager' ) }
					value={ keyword }
					onChange={ setKeyword }
				/>
				<SelectControl
					value={ memberFilter }
					onChange={ ( val ) => { setMemberFilter( val ); setPage( 1 ); } }
					options={ [
						{ label: __( 'Member / Non-member', 'conferencemanager' ), value: '' },
						{ label: __( 'Members only', 'conferencemanager' ), value: '1' },
						{ label: __( 'Non-members only', 'conferencemanager' ), value: '0' },
					] }
				/>
				<SelectControl
					value={ typeFilter }
					onChange={ setTypeFilter }
					options={ [
						{ label: __( 'All Types', 'conferencemanager' ), value: '' },
						...MEMBER_TYPES,
					] }
				/>
				<SelectControl
					value={ approvalFilter }
					onChange={ ( val ) => { setApprovalFilter( val ); setPage( 1 ); } }
					options={ [
						{ label: __( 'All approval statuses', 'conferencemanager' ), value: '' },
						{ label: __( 'Pending Approval', 'conferencemanager' ), value: 'pending' },
					] }
				/>
				<Button type="submit" className="conf-manager-btn--primary">
					{ __( 'Search', 'conferencemanager' ) }
				</Button>
				<span className="conf-manager-search__spacer" />
				{ useCan( 'conf_manager_edit_members' ) && (
					<Button className="conf-manager-btn--danger" onClick={ openCreate }>
						{ __( 'New Member', 'conferencemanager' ) }
					</Button>
				) }
				{ useCan( 'conf_manager_import_members' ) && (
					<Button className="conf-manager-btn--danger" onClick={ () => setImportOpen( true ) }>
						{ __( 'CSV Import', 'conferencemanager' ) }
					</Button>
				) }
			</form>

			<ApprovalPolicyNote />

			<DataTable
				columns={ useCan( 'conf_manager_edit_members' ) ? [
					...COLUMNS,
					{
						key: 'actions',
						label: '',
						// 🔴 編集の入口を「行クリックだけ」にしない。入口が行クリックしか
						//    無いと「操作」と「状態」の区別が画面に出ず、状態のラベルが
						//    押されてしまう。行クリックは残したまま（スマホで効く）、
						//    行末に明示的な入口を置く。
						// ⚠️ 行内ボタンの e.stopPropagation() を外さないこと。外すと
						//    ボタンの処理と行クリック（編集モーダル）が同時に走る。
						render: ( _, row ) => (
							<div className="conf-manager-row-actions">
								<Button variant="secondary" isSmall onClick={ ( e ) => { e.stopPropagation(); openEdit( row ); } }>
									{ __( 'Edit', 'conferencemanager' ) }
								</Button>
								{ row.approval_status === 'pending' && (
									<>
										<Button variant="primary" isSmall onClick={ ( e ) => { e.stopPropagation(); handleApprove( row.id ); } }>
											{ __( 'Approved', 'conferencemanager' ) }
										</Button>
										<Button variant="secondary" isDestructive isSmall onClick={ ( e ) => { e.stopPropagation(); handleReject( row.id ); } }>
											{ __( 'Rejected', 'conferencemanager' ) }
										</Button>
									</>
								) }
							</div>
						),
					},
				] : COLUMNS }
				rows={ members }
				total={ total }
				page={ page }
				perPage={ 20 }
				onPageChange={ setPage }
				onRowClick={ useCan( 'conf_manager_edit_members' ) ? openEdit : undefined }
				loading={ api.loading }
			/>

			{ isModalOpen && (
				<Modal
					title={ editingId ? __( 'Edit Member', 'conferencemanager' ) : __( 'New Member', 'conferencemanager' ) }
					onRequestClose={ () => setModalOpen( false ) }
					className="conf-manager-member-modal"
				>
					<TextControl label={ __( 'Member No.', 'conferencemanager' ) } value={ form.member_no } onChange={ updateField( 'member_no' ) } />
					<CheckboxControl
						label={ __( 'Register as member', 'conferencemanager' ) }
						checked={ form.is_member }
						onChange={ ( val ) => setForm( ( prev ) => ( {
							...prev,
							is_member: val,
							member_type: val ? ( prev.member_type === 'general' ? 'regular' : prev.member_type ) : 'general',
						} ) ) }
						help={ form.is_member ? __( 'Registered as member', 'conferencemanager' ) : __( 'Registered as non-member. Check to upgrade to member.', 'conferencemanager' ) }
					/>
					<SelectControl label={ __( 'Member Type', 'conferencemanager' ) } value={ form.member_type } onChange={ updateField( 'member_type' ) } options={ MEMBER_TYPES } />
					<TextControl label={ __( 'Last Name', 'conferencemanager' ) } value={ form.name_sei } onChange={ updateField( 'name_sei' ) } required />
					<TextControl label={ __( 'First Name', 'conferencemanager' ) } value={ form.name_mei } onChange={ updateField( 'name_mei' ) } required />
					<TextControl label={ __( 'Last Name (Kana)', 'conferencemanager' ) } value={ form.name_sei_kana } onChange={ updateField( 'name_sei_kana' ) } />
					<TextControl label={ __( 'First Name (Kana)', 'conferencemanager' ) } value={ form.name_mei_kana } onChange={ updateField( 'name_mei_kana' ) } />
					<TextControl label={ __( 'Email Address', 'conferencemanager' ) } type="email" value={ form.email } onChange={ updateField( 'email' ) } required />
					{ /* 🔴 新規作成のときだけ必須。既存会員の編集では required を付けない
					     （既に電話番号が空で登録されている会員の他の項目を直せなくしないため）。 */ }
					<TextControl label={ __( 'Phone Number', 'conferencemanager' ) } value={ form.phone } onChange={ updateField( 'phone' ) } required={ ! editingId } />
					<TextControl label={ __( 'Organization', 'conferencemanager' ) } value={ form.org_name } onChange={ updateField( 'org_name' ) } />
					<TextControl label={ __( 'Department', 'conferencemanager' ) } value={ form.org_dept } onChange={ updateField( 'org_dept' ) } />
					<TextControl label={ __( 'Address', 'conferencemanager' ) } value={ form.org_address } onChange={ updateField( 'org_address' ) } />

					<div style={ { marginTop: '16px', display: 'flex', gap: '8px' } }>
						<Button variant="primary" onClick={ handleSubmit } isBusy={ api.loading }>
							{ editingId ? __( 'Update', 'conferencemanager' ) : __( 'Create', 'conferencemanager' ) }
						</Button>
						{ editingId && (
							<Button variant="secondary" isDestructive onClick={ () => { setModalOpen( false ); handleDelete( editingId ); } }>
								{ __( 'Delete', 'conferencemanager' ) }
							</Button>
						) }
						<Button variant="tertiary" onClick={ () => setModalOpen( false ) }>
							{ __( 'Cancel', 'conferencemanager' ) }
						</Button>
					</div>
				</Modal>
			) }

			{ isImportOpen && (
				<Modal
					title={ __( 'CSV Import', 'conferencemanager' ) }
					onRequestClose={ closeImportModal }
					className="conf-manager-modal--wide"
				>
					{ /* フォーマット説明 */ }
					<div className="conf-manager-import__guide">
						<h4>{ __( 'CSV File Specifications', 'conferencemanager' ) }</h4>
						<dl className="conf-manager-import__spec">
							<dt>{ __( 'File Format', 'conferencemanager' ) }</dt>
							<dd>{ __( 'Comma-separated (.csv), double-quote enclosed', 'conferencemanager' ) }</dd>
							<dt>{ __( 'Character Encoding', 'conferencemanager' ) }</dt>
							<dd>{ __( 'UTF-8 (recommended) or Shift_JIS (Excel default)', 'conferencemanager' ) }</dd>
							<dt>{ __( 'Required Columns', 'conferencemanager' ) }</dt>
							<dd><code>name_sei</code>{ __( '(Last Name)', 'conferencemanager' ) }, <code>name_mei</code>{ __( '(First Name)', 'conferencemanager' ) }, <code>email</code></dd>
							<dt>{ __( 'Recommended Columns', 'conferencemanager' ) }</dt>
							<dd><code>member_no</code>{ __( '(Member No.)', 'conferencemanager' ) }, <code>member_type</code>{ __( '(Member Type)', 'conferencemanager' ) }, <code>name_sei_kana</code>{ __( '(Last Name Kana)', 'conferencemanager' ) }, <code>name_mei_kana</code>{ __( '(First Name Kana)', 'conferencemanager' ) }, <code>phone</code>{ __( '(Phone)', 'conferencemanager' ) }, <code>org_name</code>{ __( '(Organization)', 'conferencemanager' ) }, <code>org_dept</code>{ __( '(Department)', 'conferencemanager' ) }, <code>org_address</code>{ __( '(Address)', 'conferencemanager' ) }</dd>
							<dt>{ __( 'Columns That Are Not Imported', 'conferencemanager' ) }</dt>
							<dd>{ __( 'Any column name other than the above is silently ignored. Aliases such as tel or Japanese header names are not supported.', 'conferencemanager' ) }</dd>
							<dt>{ __( 'Duplicate Detection', 'conferencemanager' ) }</dt>
							<dd>{ __( 'Matches existing members by email address', 'conferencemanager' ) }</dd>
						</dl>
						<p className="conf-manager-import__note">
							{ __( '* The first row must be a header row. member_type values: regular / student / associate / honorary / general', 'conferencemanager' ) }
						</p>
						<p className="conf-manager-import__note">
							{ __( '* Columns other than the required ones may be left blank. When overwriting existing members, a blank cell does not clear the existing value.', 'conferencemanager' ) }
						</p>
					</div>

					{ /* メールアドレスに関する注意 */ }
					<div className="conf-manager-import__warning">
						<strong>{ __( 'Important Notes on Email Addresses', 'conferencemanager' ) }</strong>
						<ul>
							<li>{ __( 'Email addresses are used as the unique identifier for duplicate detection. Different addresses will be registered as separate members.', 'conferencemanager' ) }</li>
							<li>{ __( 'If a member\'s email address has changed, even with "overwrite existing members" enabled, it will not match the existing record and will create a duplicate. Update the address in Member Management before importing.', 'conferencemanager' ) }</li>
							<li>{ __( 'Duplicate registrations will cause incorrect registration history and certificate issuance on My Page. Always verify email address matches before importing.', 'conferencemanager' ) }</li>
							<li>{ __( 'Email addresses are case-insensitive (automatically normalized to lowercase).', 'conferencemanager' ) }</li>
							<li>{ __( 'When overwriting existing members, a blank cell leaves the existing value unchanged. To clear a value, edit that member in Member Management.', 'conferencemanager' ) }</li>
						</ul>
					</div>

					{ /* テンプレートダウンロード */ }
					<div className="conf-manager-import__template">
						<Button
							variant="link"
							onClick={ () => {
								const bom = '\uFEFF';
								// ⚠️ 列と並びは PHP の MemberImport::ALLOWED_COLUMNS と一致させること。
								//    GET /members/export も同じ並び（get_template_header()）を使うので、
								//    揃えておくとエクスポートした CSV をそのまま編集して再取込できる。
								//    ズレると「出したものが戻せない」。静的検査 csv-2 が部分集合を見る。
								const header = 'member_no,member_type,name_sei,name_mei,name_sei_kana,name_mei_kana,email,phone,org_name,org_dept,org_address';
								const sample1 = 'M-0001,regular,山田,太郎,ヤマダ,タロウ,yamada@example.com,03-1234-5678,○○大学,工学部,東京都文京区本郷7-3-1';
								const sample2 = 'M-0002,student,鈴木,花子,スズキ,ハナコ,suzuki@example.com,,△△大学,理学研究科,';
								const blob = new Blob( [ bom + header + '\n' + sample1 + '\n' + sample2 + '\n' ], { type: 'text/csv;charset=utf-8' } );
								const url = URL.createObjectURL( blob );
								const a = document.createElement( 'a' );
								a.href = url;
								a.download = 'member_import_template.csv';
								a.click();
								URL.revokeObjectURL( url );
							} }
						>
							{ __( 'Download CSV template (with 2 sample rows)', 'conferencemanager' ) }
						</Button>
					</div>

					{ /* 設定 */ }
					<div style={ { display: 'flex', gap: '16px', marginBottom: '12px', alignItems: 'flex-end' } }>
						<SelectControl
							label={ __( 'Character Encoding', 'conferencemanager' ) }
							value={ importEncoding }
							onChange={ ( v ) => { setImportEncoding( v ); setImportFile( null ); setImportPreview( null ); } }
							options={ [
								{ label: 'UTF-8', value: 'UTF-8' },
								{ label: 'Shift_JIS (Excel)', value: 'Shift_JIS' },
							] }
						/>
						<CheckboxControl
							label={ __( 'Overwrite existing members', 'conferencemanager' ) }
							checked={ importUpdateExisting }
							onChange={ setImportUpdateExisting }
						/>
					</div>

					{ /* ファイル選択 */ }
					<FormFileUpload
						accept=".csv"
						onChange={ handleImportFileSelect }
						className="conf-manager-btn--danger"
					>
						{ importFile ? importFile.name : __( 'Select CSV file', 'conferencemanager' ) }
					</FormFileUpload>

					{ /* プレビュー */ }
					{ importPreview && (
						<div className="conf-manager-import__preview">
							<h4>
								{ __( 'Preview', 'conferencemanager' ) }
								<span className="conf-manager-import__count">
									{ `${ importPreview.totalRows }${ __( ' data row(s) detected', 'conferencemanager' ) }` }
								</span>
							</h4>
							<div className="conf-manager-import__table-wrap">
								<table className="widefat striped">
									<thead>
										<tr>
											{ importPreview.header.map( ( h, i ) => <th key={ i }>{ h }</th> ) }
										</tr>
									</thead>
									<tbody>
										{ importPreview.previewRows.map( ( row, i ) => (
											<tr key={ i }>
												{ row.map( ( cell, j ) => <td key={ j }>{ cell }</td> ) }
											</tr>
										) ) }
										{ importPreview.totalRows > 5 && (
											<tr><td colSpan={ importPreview.header.length } style={ { textAlign: 'center', color: '#757575' } }>
												{ `... ${ __( 'other', 'conferencemanager' ) } ${ importPreview.totalRows - 5 } ${ __( 'items', 'conferencemanager' ) }` }
											</td></tr>
										) }
									</tbody>
								</table>
							</div>

							{ /* 実行ボタン */ }
							<div style={ { marginTop: '16px', display: 'flex', gap: '8px', alignItems: 'center' } }>
								<Button
									variant="primary"
									isDestructive
									onClick={ handleImportExecute }
									isBusy={ importLoading }
									disabled={ importLoading }
								>
									{ importLoading ? __( 'Importing...', 'conferencemanager' ) : `${ importPreview.totalRows }${ __( ' row(s) to import', 'conferencemanager' ) }` }
								</Button>
								<Button variant="tertiary" onClick={ closeImportModal } disabled={ importLoading }>
									{ __( 'Cancel', 'conferencemanager' ) }
								</Button>
								<span style={ { fontSize: '12px', color: '#757575' } }>
									{ importUpdateExisting
										? __( '* Existing members with matching emails will be overwritten', 'conferencemanager' )
										: __( '* Existing members with matching emails will be skipped', 'conferencemanager' )
									}
								</span>
							</div>
						</div>
					) }
				</Modal>
			) }
		</div>
	);
}
